From cf624f92bd3da7305afc70c6e440dab81fdf19f4 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 5 Oct 2017 09:38:45 +0200 Subject: [PATCH 01/27] TextEditor: Optimize line annotation painting Paint an annotation only if the event rectangle contains the block for that specific annotation Change-Id: I5f992f916da25268dd2c9e6b4703701934c8551d Reviewed-by: Christian Stenger --- src/plugins/texteditor/texteditor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 0fe63bbc95e..1a2ed84d6ce 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -4408,6 +4408,8 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) || d->m_blockSelection.positionColumn == d->m_blockSelection.anchorColumn) && blockSelectionCursorRect.isValid()) painter.fillRect(blockSelectionCursorRect, palette().text()); + + d->drawLineAnnotation(painter, block, lineX < viewportRect.width() ? lineX : 0); } offset.ry() += r.height(); @@ -4554,7 +4556,6 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) painter.restore(); } } - d->drawLineAnnotation(painter, block, lineX < viewportRect.width() ? lineX : 0); block = nextVisibleBlock; top = bottom; From 5fb310828eb04d373205fa48a949b6ccd10f68f4 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 5 Oct 2017 10:03:25 +0200 Subject: [PATCH 02/27] TextEditor: reduce annotation painting artifacts Request an update covering the complete annotation rectangle if it is not part of the event rectangle and Correctly calculate the position for the gradients. Task-number: QTCREATORBUG-18855 Change-Id: I68b61459e6fd94949baebba25f965226f9d5e441 Reviewed-by: Christian Stenger --- src/plugins/texteditor/texteditor.cpp | 13 +++++++++---- src/plugins/texteditor/textmark.cpp | 9 ++++++--- src/plugins/texteditor/textmark.h | 3 ++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 1a2ed84d6ce..2111ea27e59 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -396,7 +396,7 @@ public: bool expanded, bool active, bool hovered) const; - void drawLineAnnotation(QPainter &painter, const QTextBlock &block, qreal start); + void drawLineAnnotation(QPainter &painter, const QTextBlock &block, qreal start, const QRect &eventRect); void toggleBlockVisible(const QTextBlock &block); QRect foldBox(); @@ -3860,7 +3860,7 @@ QRectF TextEditorWidgetPrivate::getLastLineLineRect(const QTextBlock &block) } void TextEditorWidgetPrivate::drawLineAnnotation( - QPainter &painter, const QTextBlock &block, qreal rightMargin) + QPainter &painter, const QTextBlock &block, qreal rightMargin, const QRect &eventRect) { if (!m_displaySettings.m_displayAnnotations) return; @@ -3907,11 +3907,16 @@ void TextEditorWidgetPrivate::drawLineAnnotation( break; // paint annotation - mark->paintAnnotation(painter, &boundingRect, offset, itemOffset / 2); + mark->paintAnnotation(painter, &boundingRect, offset, itemOffset / 2, q->contentOffset()); x = boundingRect.right(); offset = itemOffset / 2; m_annotationRects[block.blockNumber()].append({boundingRect, mark}); } + + QRect updateRect(lineRect.toRect().topRight(), boundingRect.toRect().bottomRight()); + updateRect.setLeft(qMax(0, updateRect.left())); + if (!updateRect.isEmpty() && !eventRect.contains(updateRect)) + q->viewport()->update(updateRect); } void TextEditorWidget::paintEvent(QPaintEvent *e) @@ -4409,7 +4414,7 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) && blockSelectionCursorRect.isValid()) painter.fillRect(blockSelectionCursorRect, palette().text()); - d->drawLineAnnotation(painter, block, lineX < viewportRect.width() ? lineX : 0); + d->drawLineAnnotation(painter, block, lineX < viewportRect.width() ? lineX : 0, er); } offset.ry() += r.height(); diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp index d4f9db7c509..89c2385f9af 100644 --- a/src/plugins/texteditor/textmark.cpp +++ b/src/plugins/texteditor/textmark.cpp @@ -122,7 +122,8 @@ void TextMark::paintIcon(QPainter *painter, const QRect &rect) const } void TextMark::paintAnnotation(QPainter &painter, QRectF *annotationRect, - const qreal fadeInOffset, const qreal fadeOutOffset) const + const qreal fadeInOffset, const qreal fadeOutOffset, + const QPointF &contentOffset) const { QString text = lineAnnotation(); if (text.isEmpty()) @@ -136,7 +137,8 @@ void TextMark::paintAnnotation(QPainter &painter, QRectF *annotationRect, markColor, painter.background().color()); painter.save(); - QLinearGradient grad(rects.fadeInRect.topLeft(), rects.fadeInRect.topRight()); + QLinearGradient grad(rects.fadeInRect.topLeft() - contentOffset, + rects.fadeInRect.topRight() - contentOffset); grad.setColorAt(0.0, Qt::transparent); grad.setColorAt(1.0, colors.rectColor); painter.fillRect(rects.fadeInRect, grad); @@ -145,7 +147,8 @@ void TextMark::paintAnnotation(QPainter &painter, QRectF *annotationRect, paintIcon(&painter, rects.iconRect.toAlignedRect()); painter.drawText(rects.textRect, Qt::AlignLeft, rects.text); if (rects.fadeOutRect.isValid()) { - grad = QLinearGradient(rects.fadeOutRect.topLeft(), rects.fadeOutRect.topRight()); + grad = QLinearGradient(rects.fadeOutRect.topLeft() - contentOffset, + rects.fadeOutRect.topRight() - contentOffset); grad.setColorAt(0.0, colors.rectColor); grad.setColorAt(1.0, Qt::transparent); painter.fillRect(rects.fadeOutRect, grad); diff --git a/src/plugins/texteditor/textmark.h b/src/plugins/texteditor/textmark.h index d9c09ee5133..f68f92c11d9 100644 --- a/src/plugins/texteditor/textmark.h +++ b/src/plugins/texteditor/textmark.h @@ -64,7 +64,8 @@ public: virtual void paintIcon(QPainter *painter, const QRect &rect) const; virtual void paintAnnotation(QPainter &painter, QRectF *annotationRect, - const qreal fadeInOffset, const qreal fadeOutOffset) const; + const qreal fadeInOffset, const qreal fadeOutOffset, + const QPointF &contentOffset) const; struct AnnotationRects { QRectF fadeInRect; From 26d0b536cabce93bd2564bdbea0233357c50c32a Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 6 Oct 2017 14:29:13 +0200 Subject: [PATCH 03/27] TextEditor: cleanup annotation rect cache after mark is removed Change-Id: I8a0ab2df9d310da6025325ab3114eeadabfd4fb1 Reviewed-by: Christian Stenger --- src/plugins/texteditor/textdocument.cpp | 1 + src/plugins/texteditor/textdocument.h | 1 + src/plugins/texteditor/texteditor.cpp | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 1bb79b9e83a..9e8befb9391 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -959,6 +959,7 @@ void TextDocument::removeMark(TextMark *mark) } removeMarkFromMarksCache(mark); + emit markRemoved(mark); mark->setBaseTextDocument(0); updateLayout(); } diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h index 63c1f78a421..eecaa838233 100644 --- a/src/plugins/texteditor/textdocument.h +++ b/src/plugins/texteditor/textdocument.h @@ -144,6 +144,7 @@ signals: void contentsChangedWithPosition(int position, int charsRemoved, int charsAdded); void tabSettingsChanged(); void fontSettingsChanged(); + void markRemoved(TextMark *mark); protected: virtual void applyFontSettings(); diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 2111ea27e59..20b08e0df9a 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -97,6 +97,7 @@ #include #include #include +#include #include #include #include @@ -427,6 +428,7 @@ public: void slotUpdateBlockNotify(const QTextBlock &); void updateTabStops(); void applyFontSettingsDelayed(); + void markRemoved(TextMark *mark); void editorContentsChange(int position, int charsRemoved, int charsAdded); void documentAboutToBeReloaded(); @@ -673,6 +675,8 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) TextEditorWidgetPrivate::~TextEditorWidgetPrivate() { + QObject::disconnect(m_document.data(), &TextDocument::markRemoved, + this, &TextEditorWidgetPrivate::markRemoved); q->disconnect(this); delete m_toolBar; } @@ -3201,6 +3205,9 @@ void TextEditorWidgetPrivate::setupDocumentSignals() QObject::connect(m_document.data(), &TextDocument::fontSettingsChanged, this, &TextEditorWidgetPrivate::applyFontSettingsDelayed); + QObject::connect(m_document.data(), &TextDocument::markRemoved, + this, &TextEditorWidgetPrivate::markRemoved); + slotUpdateExtraAreaWidth(); TextEditorSettings *settings = TextEditorSettings::instance(); @@ -6946,6 +6953,17 @@ void TextEditorWidgetPrivate::applyFontSettingsDelayed() q->triggerPendingUpdates(); } +void TextEditorWidgetPrivate::markRemoved(TextMark *mark) +{ + auto it = m_annotationRects.find(mark->lineNumber() - 1); + if (it == m_annotationRects.end()) + return; + + Utils::erase(it.value(), [mark](AnnotationRect rect) { + return rect.mark == mark; + }); +} + void TextEditorWidget::triggerPendingUpdates() { if (d->m_fontSettingsNeedsApply) From c2686fd9ae3bc8ac1550984d88b59cb9a307c13a Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 6 Oct 2017 14:51:22 +0200 Subject: [PATCH 04/27] TextEditor: Fix annotation rectangle caching The rectangle of an annotation is cached after it was painted. If just a part of an editor was updated all annotations were removed but only redrawn annotations were added to the cache again. This behavior is replaced by removing annotations that are not visible and those which got redrawn. So annotations that are still visible but were not redrawn are kept in the cache. Change-Id: I9246f1347b8f795284fb4fc9aabe11f251d16c25 Reviewed-by: Christian Stenger --- src/plugins/texteditor/texteditor.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 20b08e0df9a..f4b70645fc2 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -4051,7 +4051,6 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) int cursor_cpos = 0; QPen cursor_pen; - d->m_annotationRects.clear(); d->m_searchResultOverlay->clear(); if (!d->m_searchExpr.isEmpty()) { // first pass for the search result overlays @@ -4206,6 +4205,12 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) fs.toTextCharFormat(C_SEARCH_RESULT).background().color(), e->rect()); + { // remove all annotation rects from the cache that where drawn before the first visible block + auto it = d->m_annotationRects.begin(); + auto end = d->m_annotationRects.end(); + while (it != end && it.key() < block.blockNumber()) + it = d->m_annotationRects.erase(it); + } while (block.isValid()) { @@ -4421,6 +4426,7 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) && blockSelectionCursorRect.isValid()) painter.fillRect(blockSelectionCursorRect, palette().text()); + d->m_annotationRects.remove(block.blockNumber()); d->drawLineAnnotation(painter, block, lineX < viewportRect.width() ? lineX : 0, er); } @@ -4441,6 +4447,19 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) block = doc->findBlockByLineNumber(block.firstLineNumber()); } } + + // remove all annotation rects from the cache that where drawn after the last visible block + if (block.isValid()) { + auto it = d->m_annotationRects.begin(); + auto end = d->m_annotationRects.end(); + while (it != end) { + if (it.key() > block.blockNumber()) + it = d->m_annotationRects.erase(it); + else + ++it; + } + } + painter.setPen(context.palette.text().color()); if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom() From faee18d31e3c34bb9f13be62bfdf2011cca4c67c Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 29 Sep 2017 15:01:32 +0200 Subject: [PATCH 05/27] AutoTest: Enable debugging for Quick Tests Task-number: QTCREATORBUG-18961 Change-Id: I6f00c84ea6279ceb59745e5bb12349511ae4637b Reviewed-by: David Schulz --- src/plugins/autotest/qtest/qttestconfiguration.cpp | 6 ++---- .../autotest/quick/quicktestconfiguration.cpp | 10 ++++++++++ src/plugins/autotest/quick/quicktestconfiguration.h | 4 ++-- src/plugins/autotest/quick/quicktesttreeitem.cpp | 13 +++++++++++++ src/plugins/autotest/quick/quicktesttreeitem.h | 2 ++ src/plugins/autotest/testconfiguration.h | 3 +++ src/plugins/autotest/testrunconfiguration.h | 8 +++++--- 7 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/plugins/autotest/qtest/qttestconfiguration.cpp b/src/plugins/autotest/qtest/qttestconfiguration.cpp index 157c9873e58..46a6a3d99f6 100644 --- a/src/plugins/autotest/qtest/qttestconfiguration.cpp +++ b/src/plugins/autotest/qtest/qttestconfiguration.cpp @@ -81,10 +81,8 @@ QStringList QtTestConfiguration::argumentsForTestRunner(QStringList *omitted) co if (qtSettings->logSignalsSlots) arguments << "-vs"; - if (isDebugRunMode()) { - if (qtSettings->noCrashHandler) - arguments << "-nocrashhandler"; - } + if (isDebugRunMode() && qtSettings->noCrashHandler) + arguments << "-nocrashhandler"; return arguments; } diff --git a/src/plugins/autotest/quick/quicktestconfiguration.cpp b/src/plugins/autotest/quick/quicktestconfiguration.cpp index 827b350cfe6..6875207bf76 100644 --- a/src/plugins/autotest/quick/quicktestconfiguration.cpp +++ b/src/plugins/autotest/quick/quicktestconfiguration.cpp @@ -35,6 +35,11 @@ namespace Autotest { namespace Internal { +QuickTestConfiguration::QuickTestConfiguration() +{ + setMixedDebugging(true); +} + TestOutputReader *QuickTestConfiguration::outputReader(const QFutureInterface &fi, QProcess *app) const { @@ -74,6 +79,11 @@ QStringList QuickTestConfiguration::argumentsForTestRunner(QStringList *omitted) const QString &metricsOption = QtTestSettings::metricsTypeToOption(qtSettings->metrics); if (!metricsOption.isEmpty()) arguments << metricsOption; + + if (isDebugRunMode()) { + if (qtSettings->noCrashHandler) + arguments << "-nocrashhandler"; + } return arguments; } diff --git a/src/plugins/autotest/quick/quicktestconfiguration.h b/src/plugins/autotest/quick/quicktestconfiguration.h index ecab2f4a2f3..7eca4cc1f01 100644 --- a/src/plugins/autotest/quick/quicktestconfiguration.h +++ b/src/plugins/autotest/quick/quicktestconfiguration.h @@ -30,10 +30,10 @@ namespace Autotest { namespace Internal { -class QuickTestConfiguration : public TestConfiguration +class QuickTestConfiguration : public DebuggableTestConfiguration { public: - explicit QuickTestConfiguration() {} + QuickTestConfiguration(); TestOutputReader *outputReader(const QFutureInterface &fi, QProcess *app) const override; QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override; diff --git a/src/plugins/autotest/quick/quicktesttreeitem.cpp b/src/plugins/autotest/quick/quicktesttreeitem.cpp index 35202696364..953f31e6618 100644 --- a/src/plugins/autotest/quick/quicktesttreeitem.cpp +++ b/src/plugins/autotest/quick/quicktesttreeitem.cpp @@ -109,6 +109,11 @@ bool QuickTestTreeItem::canProvideTestConfiguration() const } } +bool QuickTestTreeItem::canProvideDebugConfiguration() const +{ + return canProvideTestConfiguration(); +} + TestConfiguration *QuickTestTreeItem::testConfiguration() const { ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); @@ -143,6 +148,14 @@ TestConfiguration *QuickTestTreeItem::testConfiguration() const return config; } +TestConfiguration *QuickTestTreeItem::debugConfiguration() const +{ + QuickTestConfiguration *config = static_cast(testConfiguration()); + if (config) + config->setRunMode(TestRunMode::Debug); + return config; +} + QList QuickTestTreeItem::getAllTestConfigurations() const { QList result; diff --git a/src/plugins/autotest/quick/quicktesttreeitem.h b/src/plugins/autotest/quick/quicktesttreeitem.h index a0e860a52c0..be036a13fb3 100644 --- a/src/plugins/autotest/quick/quicktesttreeitem.h +++ b/src/plugins/autotest/quick/quicktesttreeitem.h @@ -39,7 +39,9 @@ public: QVariant data(int column, int role) const override; Qt::ItemFlags flags(int column) const override; bool canProvideTestConfiguration() const override; + bool canProvideDebugConfiguration() const override; TestConfiguration *testConfiguration() const override; + TestConfiguration *debugConfiguration() const override; QList getAllTestConfigurations() const override; QList getSelectedTestConfigurations() const override; TestTreeItem *find(const TestParseResult *result) override; diff --git a/src/plugins/autotest/testconfiguration.h b/src/plugins/autotest/testconfiguration.h index e35b0f0dd21..e3a0bcea3f9 100644 --- a/src/plugins/autotest/testconfiguration.h +++ b/src/plugins/autotest/testconfiguration.h @@ -117,8 +117,11 @@ public: void setRunMode(TestRunMode mode) { m_runMode = mode; } TestRunMode runMode() const { return m_runMode; } bool isDebugRunMode() const; + void setMixedDebugging(bool enable) { m_mixedDebugging = enable; } + bool mixedDebugging() const { return m_mixedDebugging; } private: TestRunMode m_runMode; + bool m_mixedDebugging = false; }; } // namespace Internal diff --git a/src/plugins/autotest/testrunconfiguration.h b/src/plugins/autotest/testrunconfiguration.h index b0cd1c8e1d2..34ba6b8363b 100644 --- a/src/plugins/autotest/testrunconfiguration.h +++ b/src/plugins/autotest/testrunconfiguration.h @@ -52,10 +52,12 @@ public: initialize("AutoTest.TestRunConfig"); setDefaultDisplayName(tr("AutoTest Debug")); - // disable QmlDebugger that is enabled by default - // might change if debugging QuickTest gets enabled + bool enableQuick = false; + if (auto debuggable = dynamic_cast(config)) + enableQuick = debuggable->mixedDebugging(); + if (auto debugAspect = extraAspect()) - debugAspect->setUseQmlDebugger(false); + debugAspect->setUseQmlDebugger(enableQuick); m_testConfig = config; } From 9a7e1cd6c69edab7e5992f9fe59b29672922bb2b Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Fri, 6 Oct 2017 17:58:08 +0200 Subject: [PATCH 06/27] VcsBase: Modernize the icons Use themable, HighDPI enabled icons for commit and diff. Re-use an existing red cross for the removesubmitfield. Change-Id: I43f46db96d07de9a7e166937d470d48e45d7f3fb Reviewed-by: Orgad Shaneh --- src/plugins/vcsbase/images/diff.png | Bin 204 -> 0 bytes src/plugins/vcsbase/images/diff_arrows.png | Bin 0 -> 155 bytes src/plugins/vcsbase/images/diff_arrows@2x.png | Bin 0 -> 232 bytes src/plugins/vcsbase/images/diff_documents.png | Bin 0 -> 159 bytes .../vcsbase/images/diff_documents@2x.png | Bin 0 -> 192 bytes .../vcsbase/images/removesubmitfield.png | Bin 578 -> 0 bytes src/plugins/vcsbase/images/submit.png | Bin 309 -> 0 bytes src/plugins/vcsbase/images/submit_arrow.png | Bin 0 -> 123 bytes .../vcsbase/images/submit_arrow@2x.png | Bin 0 -> 155 bytes src/plugins/vcsbase/images/submit_db.png | Bin 0 -> 167 bytes src/plugins/vcsbase/images/submit_db@2x.png | Bin 0 -> 292 bytes src/plugins/vcsbase/submitfieldwidget.cpp | 4 +- src/plugins/vcsbase/vcsbase.qbs | 3 - src/plugins/vcsbase/vcsbase.qrc | 11 +- src/plugins/vcsbase/vcsbasesubmiteditor.cpp | 14 ++- src/tools/icons/qtcreatoricons.svg | 111 ++++++++++++++++++ 16 files changed, 134 insertions(+), 9 deletions(-) delete mode 100644 src/plugins/vcsbase/images/diff.png create mode 100644 src/plugins/vcsbase/images/diff_arrows.png create mode 100644 src/plugins/vcsbase/images/diff_arrows@2x.png create mode 100644 src/plugins/vcsbase/images/diff_documents.png create mode 100644 src/plugins/vcsbase/images/diff_documents@2x.png delete mode 100644 src/plugins/vcsbase/images/removesubmitfield.png delete mode 100644 src/plugins/vcsbase/images/submit.png create mode 100644 src/plugins/vcsbase/images/submit_arrow.png create mode 100644 src/plugins/vcsbase/images/submit_arrow@2x.png create mode 100644 src/plugins/vcsbase/images/submit_db.png create mode 100644 src/plugins/vcsbase/images/submit_db@2x.png diff --git a/src/plugins/vcsbase/images/diff.png b/src/plugins/vcsbase/images/diff.png deleted file mode 100644 index b3597f9ff850d9f2ddc10b1ff73162d85a48e8db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 204 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7CVRR#hFJ72ophJ?fC5KL z|1xI7o@A>Y<{jL%yV@27U5-n7BriXK`G+PWL#S!}%_!k_yawMR4JNOOdjB_0&|^Y- z;BlUaY}r*w-#BJ6h0WSp@Q&^GnLFGHw?&Vnzvfhq zorFwcr1rgMllP|Trbxw9oSw%Nc6nN+gi6%YcGer4e@@oBvOeJPqqCv83=9kmp00i_ I>zopr05Ypj^#A|> diff --git a/src/plugins/vcsbase/images/diff_arrows.png b/src/plugins/vcsbase/images/diff_arrows.png new file mode 100644 index 0000000000000000000000000000000000000000..f3c89ca292d9a6ca053d73fa05dfb2647dcd6e05 GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4h9AW2CEqh_A)RqBzd|xhDc0J{?X4Skzmrk zAm&W5O5=a~`*-#EoWvNK%$qcr_@_!pNIZF`w34Cg#68!GpAP)9TP3M+O^Ms4WJkBs zhXBD0vq=nB%tVY??k$p(3Ml%2H$SEE`NmI8Z+QL}L~t|AxP9M1eX+%9kgcAselF{r G5}E*23^uO- literal 0 HcmV?d00001 diff --git a/src/plugins/vcsbase/images/diff_arrows@2x.png b/src/plugins/vcsbase/images/diff_arrows@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d2fa19278f68b17e39250093370f32917554d291 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4h9AWhA=@@4F(2=^`0({Ar_~TfA}*POjyyt z$ibpw+AJ0SAdrD=&g8=AkMC|S4K?)XQUADm;T%Qr{Y}9^;@&!_Hp9Bn&=-(9i4@%WKz j@Pal8eL2>Y<7y1^zt_C^v~==KkQY2%{an^LB{Ts5(`{If literal 0 HcmV?d00001 diff --git a/src/plugins/vcsbase/images/diff_documents.png b/src/plugins/vcsbase/images/diff_documents.png new file mode 100644 index 0000000000000000000000000000000000000000..5db2b6ccbfd0e18505e1e203d7c211fd146c5118 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdu3Nd_r7(eSK?dYwPOj8X6j! zo0}g$e*Em&vtPe{{r>&?|Ns9@4VwcQ7#M6kT^vI=qLXKEvhoN^*evK_dFrL1d_nkB z!h;rrOP4knI9N5_lC*HNl5{wvFgJjWS-piZq`mtfLnt#7!^JGCi>AE4br={J7(8A5 KT-G@yGywoNH#r~x literal 0 HcmV?d00001 diff --git a/src/plugins/vcsbase/images/diff_documents@2x.png b/src/plugins/vcsbase/images/diff_documents@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cf5d2ce551715cdfee86efd42aeab6ed44fbe421 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I7G?$phQ^Te;|vT8(g8jpuD-s$85tRwnVC5` zIX7?KeD>_wyLazCeE9JH|Novvx|V!3?jB`6O0r vHca1Un_H=O_N$Cs0!+rfR(?DT42;KO|p}-@cXQDHG5+e5;DEpzx}FZT-eMKbHwpge1c32OZ%$@|69r~A5@KQS9ts2 zl|pDiMv$}fLcSuly~fk0Tv+mw_j>>NqcR`o-h244@%!=BoQhwh4}1$<^eSKTx^(X` zbDv&;Lz)7zZb8?W61u1HK7L!Pxtjm^^M`%M%TI2K+V_6<|3gzl6P_>4e6{j&@N;(& z!>Bp4Z|}bQGC`T6zu)4x(8#iCiu<}C|a`gE~!#b3LqnLUfAT9&W-TD7~} zX8o6xG`77Oscg-c)a#NQuJ-m>(FlwgHk`+J8q?oJ%(ixTDK z7u(4!%DbJBzS%+G!L^K*qfUy~F27s5W<~m4y=!jDdnOxQV`%8Sm#nnKNOI4)=Z+1LhFJ6-ourt3#6h5S zzl`9*MHAu!ieT|tGfd`f*ezP9ezv8wd-p~8TyU))ZYmK@2w zs;645^6bGpskD35XKu^xFxg}2G;3Maozt7ny!vCgQQ*-P59!HA|G!Jyex>}R*ZB@X zryFPI@Gi1o2-~$v%Uf=-j`x{6_uowOJEp-mVT+2V)4b<>juLI#zA@(It(OTBVYf;D zo^!o=*Sf{I(ZBX>-+J&+V4qLiJ^5uCm)SN++*tR0GMn9j?;o$5`N%}_*eqRd;%l7y z;oFbY{zEn^blB?nu1|JiR&UZ_v-{-lulaQDFUE#l>jJ$h3)+JB+5BVJ7@6=-%|Squ Qfq{X+)78&qol`;+0Ah@Z0ssI2 diff --git a/src/plugins/vcsbase/images/submit_arrow.png b/src/plugins/vcsbase/images/submit_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..5d849210c359fb206b41e497a6ef817b749e0556 GIT binary patch literal 123 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdqsCd_r6q7#QT_#z@Xyk;uyj)Gnu26iS3evV%M?&LG?rf<%Y(_iz2Oz)188RE~GH< YiKspQa$EHf0|Nttr>mdKI;Vst0O3p}IRF3v literal 0 HcmV?d00001 diff --git a/src/plugins/vcsbase/images/submit_arrow@2x.png b/src/plugins/vcsbase/images/submit_arrow@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..78064174dbd49f01f04bfcadf21b8bfb273f0f88 GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I7G?$phQ^Te;|vT8LIFM@t_%ze<>lq|_4N%6 z4ILdF|NsAg?_Io}fq}ur)5S4FV`4IgD>ILCv_eaxpl>U4v&JfhO+p=#9$peok_Q?D zV;R=5=p}Ti9q7qg!4$*jqQJ3`L8)O0gHFSmgbOX243n?V=3JK+WX{0Az~JfX=d#Wz Gp$Pzrdo73n literal 0 HcmV?d00001 diff --git a/src/plugins/vcsbase/images/submit_db.png b/src/plugins/vcsbase/images/submit_db.png new file mode 100644 index 0000000000000000000000000000000000000000..2796c6576f82d56e4ef1a93aa8688b4587cbe189 GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4h9AW2CEqh_A)Rq^xLVf^?s;hz!bj00RBH~Ahno7%a)#rki^4w>CCM>K8bH8V4ObUt}r S>nODNbFP1DYKy?hjbnSbIa11P^tEuD^BRkzEeY)K^|QNB7s>UljDd zen&lL9YbGlMTf?ejCo5Q2pzZfyv-@bvh;?mk=Yv&(TW)yet8+2uNu3Fm54cTwu*}L z?qUe(sWfm3U=U!GQ8?c`heaWKkNnI=mK_hzm$UWo-kQa)!szzpbKu6EED_h1$OXpg znMTcznkMq$r@z3FzWMpGr!NIQc4QT0oWuF+q3ONpj@u$EpE)kLc7MjRUE!e*Vy(QZ teQV1m?(@v9&s06^@-XD|zq>CxD;e%xRck)hHgzA!`<||TF6*2UngCbIagYE2 literal 0 HcmV?d00001 diff --git a/src/plugins/vcsbase/submitfieldwidget.cpp b/src/plugins/vcsbase/submitfieldwidget.cpp index 201f260e6b1..95127f7357b 100644 --- a/src/plugins/vcsbase/submitfieldwidget.cpp +++ b/src/plugins/vcsbase/submitfieldwidget.cpp @@ -25,6 +25,8 @@ #include "submitfieldwidget.h" +#include + #include #include #include @@ -139,7 +141,7 @@ struct SubmitFieldWidgetPrivate { }; SubmitFieldWidgetPrivate::SubmitFieldWidgetPrivate() : - removeFieldIcon(QLatin1String(":/vcsbase/images/removesubmitfield.png")), + removeFieldIcon(Utils::Icons::BROKEN.icon()), completer(0), hasBrowseButton(false), allowDuplicateFields(false), diff --git a/src/plugins/vcsbase/vcsbase.qbs b/src/plugins/vcsbase/vcsbase.qbs index db5c5a37393..bfaf329f6e4 100644 --- a/src/plugins/vcsbase/vcsbase.qbs +++ b/src/plugins/vcsbase/vcsbase.qbs @@ -74,9 +74,6 @@ QtcPlugin { "vcsplugin.h", "vcsprojectcache.cpp", "vcsprojectcache.h", - "images/diff.png", - "images/removesubmitfield.png", - "images/submit.png", "wizard/vcsconfigurationpage.cpp", "wizard/vcsconfigurationpage.h", "wizard/vcscommandpage.cpp", diff --git a/src/plugins/vcsbase/vcsbase.qrc b/src/plugins/vcsbase/vcsbase.qrc index 4234890c3a5..101764d9f21 100644 --- a/src/plugins/vcsbase/vcsbase.qrc +++ b/src/plugins/vcsbase/vcsbase.qrc @@ -1,8 +1,13 @@ images/category_vcs.png - images/diff.png - images/removesubmitfield.png - images/submit.png + images/diff_arrows.png + images/diff_arrows@2x.png + images/diff_documents.png + images/diff_documents@2x.png + images/submit_arrow.png + images/submit_arrow@2x.png + images/submit_db.png + images/submit_db@2x.png diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index a1c01d7a96e..02a4b4783fd 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include #include @@ -715,12 +717,20 @@ bool VcsBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript QIcon VcsBaseSubmitEditor::diffIcon() { - return QIcon(QLatin1String(":/vcsbase/images/diff.png")); + using namespace Utils; + return Icon({ + {":/vcsbase/images/diff_documents.png", Theme::PanelTextColorDark}, + {":/vcsbase/images/diff_arrows.png", Theme::IconsStopColor} + }, Icon::Tint).icon(); } QIcon VcsBaseSubmitEditor::submitIcon() { - return QIcon(QLatin1String(":/vcsbase/images/submit.png")); + using namespace Utils; + return Icon({ + {":/vcsbase/images/submit_db.png", Theme::PanelTextColorDark}, + {":/vcsbase/images/submit_arrow.png", Theme::IconsRunColor} + }, Icon::Tint | Icon::PunchEdges).icon(); } // Reduce a list of untracked files reported by a VCS down to the files diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 1703885bc5b..86c713da25e 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -8754,4 +8754,115 @@ x="0" /> + + + + + + + + + + + + + + + + + + + + + + + + From 4a3765d64ce7f3fac9b791f5ec1c3719efb53d27 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 10 Oct 2017 08:56:05 +0200 Subject: [PATCH 07/27] TextEditor: Use visual whitespace format for line wrap arrow Change-Id: Ifbf31010171ea4fd762457337ed56f60c19d5b4c Reviewed-by: Christian Stenger --- src/plugins/texteditor/texteditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index f4b70645fc2..5da5372bd10 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -4498,7 +4498,7 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) int lineCount = layout->lineCount(); if (lineCount >= 2 || !nextBlock.isValid()) { painter.save(); - painter.setPen(Qt::lightGray); + painter.setPen(fs.toTextCharFormat(C_VISUAL_WHITESPACE).foreground().color()); for (int i = 0; i < lineCount-1; ++i) { // paint line wrap indicator QTextLine line = layout->lineAt(i); QRectF lineRect = line.naturalTextRect().translated(offset.x(), top); From c3688b901f320283f026d777aee04dcbc2560892 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Tue, 10 Oct 2017 13:01:06 +0200 Subject: [PATCH 08/27] Ios: Fix incorrect Xcode version detected pkgutil always return the Xcode path installed via app store Task-number: QTCREATORBUG-18091 Change-Id: I47b5a9c3f3a482feea2dc903dbff3441a3930ab6 Reviewed-by: Jake Petroules --- src/plugins/ios/iosconfigurations.cpp | 30 +++++++-------------------- src/plugins/ios/iosrunner.cpp | 19 ++--------------- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp index 68680911154..4e8dd41aa25 100644 --- a/src/plugins/ios/iosconfigurations.cpp +++ b/src/plugins/ios/iosconfigurations.cpp @@ -210,30 +210,16 @@ static void setupKit(Kit *kit, Core::Id pDeviceType, const ToolChainPair& toolCh SysRootKitInformation::setSysRoot(kit, sdkPath); } -static QVersionNumber findXcodeVersion() +static QVersionNumber findXcodeVersion(const Utils::FileName &developerPath) { - Utils::SynchronousProcess pkgUtilProcess; - Utils::SynchronousProcessResponse resp = - pkgUtilProcess.runBlocking("pkgutil", QStringList("--pkg-info-plist=com.apple.pkg.Xcode")); - if (resp.result == Utils::SynchronousProcessResponse::Finished) { - QDomDocument xcodeVersionDoc; - if (xcodeVersionDoc.setContent(resp.allRawOutput())) { - QDomNodeList nodes = xcodeVersionDoc.elementsByTagName(QStringLiteral("key")); - for (int i = 0; i < nodes.count(); ++i) { - QDomElement elem = nodes.at(i).toElement(); - if (elem.text().compare(QStringLiteral("pkg-version")) == 0) { - QString versionStr = elem.nextSiblingElement().text(); - return QVersionNumber::fromString(versionStr); - } - } - } else { - qCDebug(iosCommonLog) << "Error finding Xcode version. Cannot parse xml output from pkgutil."; - } + FileName xcodeInfo = developerPath.parentDir().appendPath("Info.plist"); + if (xcodeInfo.exists()) { + QSettings settings(xcodeInfo.toString(), QSettings::NativeFormat); + return QVersionNumber::fromString(settings.value("CFBundleShortVersionString").toString()); } else { - qCDebug(iosCommonLog) << "Error finding Xcode version. pkgutil command failed."; + qCDebug(iosCommonLog) << "Error finding Xcode version." << xcodeInfo.toUserOutput() << + "does not exist."; } - - qCDebug(iosCommonLog) << "Error finding Xcode version. Unknow error."; return QVersionNumber(); } @@ -439,7 +425,7 @@ void IosConfigurations::setDeveloperPath(const FileName &devPath) m_instance->updateSimulators(); // Find xcode version. - m_instance->m_xcodeVersion = findXcodeVersion(); + m_instance->m_xcodeVersion = findXcodeVersion(m_instance->m_developerPath); } } } diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp index 18264317bb8..7f993865ba6 100644 --- a/src/plugins/ios/iosrunner.cpp +++ b/src/plugins/ios/iosrunner.cpp @@ -486,26 +486,11 @@ void IosDebugSupport::start() setInferiorExecutable(iosRunConfig->localExecutable().toString()); setRemoteChannel("connect://localhost:" + gdbServerPort.toString()); - FileName xcodeInfo = IosConfigurations::developerPath().parentDir().appendPath("Info.plist"); - bool buggyLldb = false; - if (xcodeInfo.exists()) { - QSettings settings(xcodeInfo.toString(), QSettings::NativeFormat); - QStringList version = settings.value(QLatin1String("CFBundleShortVersionString")).toString() - .split('.'); - if (version.value(0).toInt() == 5 && version.value(1, QString::number(1)).toInt() == 0) - buggyLldb = true; - } QString bundlePath = iosRunConfig->bundleDirectory().toString(); bundlePath.chop(4); FileName dsymPath = FileName::fromString(bundlePath.append(".dSYM")); - if (!dsymPath.exists()) { - if (buggyLldb) - TaskHub::addTask(Task::Warning, - tr("Debugging with Xcode 5.0.x can be unreliable without a dSYM. " - "To create one, add a dsymutil deploystep."), - ProjectExplorer::Constants::TASK_CATEGORY_DEPLOYMENT); - } else if (dsymPath.toFileInfo().lastModified() - < QFileInfo(iosRunConfig->localExecutable().toUserOutput()).lastModified()) { + if (dsymPath.exists() && dsymPath.toFileInfo().lastModified() + < QFileInfo(iosRunConfig->localExecutable().toUserOutput()).lastModified()) { TaskHub::addTask(Task::Warning, tr("The dSYM %1 seems to be outdated, it might confuse the debugger.") .arg(dsymPath.toUserOutput()), From 1748195e5081454fff503a295b6c5d37ce94a8ba Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Tue, 10 Oct 2017 00:12:57 +0200 Subject: [PATCH 09/27] Utils: Add an icon for "Project" Whether it is should be the wrench or the screw-nut is currently unclear. Let's make it a wrench for now. Change-Id: I8688e808dab0f522d7fee59885ae443ea0b67db1 Reviewed-by: Eike Ziller --- src/libs/utils/images/project.png | Bin 0 -> 242 bytes src/libs/utils/images/project@2x.png | Bin 0 -> 384 bytes src/libs/utils/utils.qrc | 2 ++ src/libs/utils/utilsicons.cpp | 2 ++ src/libs/utils/utilsicons.h | 1 + src/tools/icons/qtcreatoricons.svg | 19 +++++++++++++++++++ 6 files changed, 24 insertions(+) create mode 100644 src/libs/utils/images/project.png create mode 100644 src/libs/utils/images/project@2x.png diff --git a/src/libs/utils/images/project.png b/src/libs/utils/images/project.png new file mode 100644 index 0000000000000000000000000000000000000000..f43b78dee09dddfb1ce0d0e3e2e9327c6dcfe814 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QUdu|NsB@ z@85s$;K79p7cO18R9#(NS6A2C+FD&*9T5?stgNh}qQcJ3&dtru!^6YCz%bwL?R5qQ z2Emdbzu%$tvCiY0J!&2(Zk5s^A1p^;f4 wku4&%^w?@^o!fU}rB*$zPG0}>damdKI;Vst0J)82g#Z8m literal 0 HcmV?d00001 diff --git a/src/libs/utils/images/project@2x.png b/src/libs/utils/images/project@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..432016e10f9d112bb759b29019d611d05ac31955 GIT binary patch literal 384 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tG(gJ)!Tz~!g_2eLxC zX3U&9v%0#vxw*NerKP&M+TY*b$;nAZMn*tDKu}OnSXfv@M1+BXfrW*Iot^!J8q)&? z1_t?(Aiv;uAn^Xf$B&;sfBpgjU%!6+_U+sEpT9tE_~&k$9L~VNu*%cLF~s8Z*~=&S znhZo7E;faP6gadwx$6J>@4u@-Yf0^nE9*SoalTwJN#FUe=7rxz{;2}j%@1@aa)IM@9-jDp_KhxQ27DB&(q&6ky`TZ-v!UR ljk)GGPXF2(zQ4Lke|w11gsjPe(F_a>44$rjF6*2UngE!2yAJ>W literal 0 HcmV?d00001 diff --git a/src/libs/utils/utils.qrc b/src/libs/utils/utils.qrc index 63ca09e10f5..9086ea94a77 100644 --- a/src/libs/utils/utils.qrc +++ b/src/libs/utils/utils.qrc @@ -176,5 +176,7 @@ images/pan@2x.png images/wizardicon-file.png images/wizardicon-file@2x.png + images/project.png + images/project@2x.png diff --git a/src/libs/utils/utilsicons.cpp b/src/libs/utils/utilsicons.cpp index 271d3122f66..cc08c3920c4 100644 --- a/src/libs/utils/utilsicons.cpp +++ b/src/libs/utils/utilsicons.cpp @@ -46,6 +46,8 @@ const Icon PREV({ {QLatin1String(":/utils/images/prev.png"), Theme::IconsWarningColor}}, Icon::MenuTintedStyle); const Icon PREV_TOOLBAR({ {QLatin1String(":/utils/images/prev.png"), Theme::IconsNavigationArrowsColor}}); +const Icon PROJECT({ + {QLatin1String(":/utils/images/project.png"), Theme::PanelTextColorDark}}, Icon::Tint); const Icon ZOOM({ {QLatin1String(":/utils/images/zoom.png"), Theme::PanelTextColorMid}}, Icon::Tint); const Icon ZOOM_TOOLBAR({ diff --git a/src/libs/utils/utilsicons.h b/src/libs/utils/utilsicons.h index 5725e9a5382..af618ee4546 100644 --- a/src/libs/utils/utilsicons.h +++ b/src/libs/utils/utilsicons.h @@ -40,6 +40,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon NEXT; QTCREATOR_UTILS_EXPORT extern const Icon NEXT_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon PREV; QTCREATOR_UTILS_EXPORT extern const Icon PREV_TOOLBAR; +QTCREATOR_UTILS_EXPORT extern const Icon PROJECT; QTCREATOR_UTILS_EXPORT extern const Icon ZOOM; QTCREATOR_UTILS_EXPORT extern const Icon ZOOM_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon ZOOMIN_TOOLBAR; diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 86c713da25e..442a2657492 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -8865,4 +8865,23 @@ height="100%" transform="rotate(180,1732.0027,576.00257)" /> + + + + From 409e708720aecdf5273ab725a6cc7d6e34dab780 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Tue, 10 Oct 2017 15:08:40 +0200 Subject: [PATCH 10/27] Fix abi not found for Qt version Task-number: QTCREATORBUG-19066 Change-Id: I618347d81396a779de3f7b02da6f5f363a22ae88 Reviewed-by: BogDan Vatra Reviewed-by: Orgad Shaneh --- src/plugins/qtsupport/baseqtversion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 437fab28286..e6c5517963d 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -1708,7 +1708,7 @@ FileNameList BaseQtVersion::qtCorePaths() const if (dir.isEmpty()) continue; QDir d(dir); - QFileInfoList infoList = d.entryInfoList(QDir::NoDotAndDotDot); + QFileInfoList infoList = d.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); foreach (const QFileInfo &info, infoList) { const QString file = info.fileName(); if (info.isDir() From 679cee878db1eac305e42e305da91570cf164e36 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 11 Oct 2017 05:28:41 +0300 Subject: [PATCH 11/27] VCS: Clean up QLatin1String in VcsBasePlugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I80e01ee352c5a973b5b84537df7b4170b6e1f036 Reviewed-by: André Hartmann --- src/plugins/vcsbase/vcsbaseplugin.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp index 92b2fb44a38..acc3e312ad1 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.cpp +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -269,8 +269,8 @@ void StateListener::slotStateChanged() if (currentFi.exists()) { // Quick check: Does it look like a patch? - const bool isPatch = state.currentFile.endsWith(QLatin1String(".patch")) - || state.currentFile.endsWith(QLatin1String(".diff")); + const bool isPatch = state.currentFile.endsWith(".patch") + || state.currentFile.endsWith(".diff"); if (isPatch) { // Patch: Figure out a name to display. If it is a temp file, it could be // Codepaster. Use the display name of the editor. @@ -787,9 +787,9 @@ void VcsBasePlugin::setProcessEnvironment(QProcessEnvironment *e, const QString &sshPromptBinary) { if (forceCLocale) - e->insert(QLatin1String("LANG"), QString(QLatin1Char('C'))); + e->insert("LANG", "C"); if (!sshPromptBinary.isEmpty()) - e->insert(QLatin1String("SSH_ASKPASS"), sshPromptBinary); + e->insert("SSH_ASKPASS", sshPromptBinary); } // Run a process synchronously, returning Utils::SynchronousProcessResponse From 1932912e7c07484fa3523f451a9317630b9aac20 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 9 Oct 2017 06:59:54 +0200 Subject: [PATCH 12/27] Add icons to file system view directory selector Change-Id: I0b6451d7f68fae93e0b140aaea030c88ec1a8801 Reviewed-by: Eike Ziller Reviewed-by: Alessandro Portale --- src/{plugins/help => libs/utils}/images/home.png | Bin src/{plugins/help => libs/utils}/images/home@2x.png | Bin src/libs/utils/utils.qrc | 2 ++ src/libs/utils/utilsicons.cpp | 5 +++++ src/libs/utils/utilsicons.h | 2 ++ src/plugins/help/help.qrc | 2 -- src/plugins/help/helpwidget.cpp | 2 +- .../projectexplorer/foldernavigationwidget.cpp | 11 ++++++++--- .../projectexplorer/foldernavigationwidget.h | 2 ++ src/plugins/projectexplorer/session.cpp | 9 +++++++-- src/shared/help/helpicons.h | 2 -- src/tools/icons/qtcreatoricons.svg | 2 +- 12 files changed, 28 insertions(+), 11 deletions(-) rename src/{plugins/help => libs/utils}/images/home.png (100%) rename src/{plugins/help => libs/utils}/images/home@2x.png (100%) diff --git a/src/plugins/help/images/home.png b/src/libs/utils/images/home.png similarity index 100% rename from src/plugins/help/images/home.png rename to src/libs/utils/images/home.png diff --git a/src/plugins/help/images/home@2x.png b/src/libs/utils/images/home@2x.png similarity index 100% rename from src/plugins/help/images/home@2x.png rename to src/libs/utils/images/home@2x.png diff --git a/src/libs/utils/utils.qrc b/src/libs/utils/utils.qrc index 9086ea94a77..95a473da2ab 100644 --- a/src/libs/utils/utils.qrc +++ b/src/libs/utils/utils.qrc @@ -178,5 +178,7 @@ images/wizardicon-file@2x.png images/project.png images/project@2x.png + images/home.png + images/home@2x.png diff --git a/src/libs/utils/utilsicons.cpp b/src/libs/utils/utilsicons.cpp index cc08c3920c4..016d3d5fb46 100644 --- a/src/libs/utils/utilsicons.cpp +++ b/src/libs/utils/utilsicons.cpp @@ -28,6 +28,11 @@ namespace Utils { namespace Icons { + +const Icon HOME({ + {QLatin1String(":/utils/images/home.png"), Utils::Theme::PanelTextColorDark}}, Icon::Tint); +const Icon HOME_TOOLBAR({ + {QLatin1String(":/utils/images/home.png"), Utils::Theme::IconsBaseColor}}); const Icon EDIT_CLEAR({ {QLatin1String(":/utils/images/editclear.png"), Theme::PanelTextColorMid}}, Icon::Tint); const Icon EDIT_CLEAR_TOOLBAR({ diff --git a/src/libs/utils/utilsicons.h b/src/libs/utils/utilsicons.h index af618ee4546..9a3a586e147 100644 --- a/src/libs/utils/utilsicons.h +++ b/src/libs/utils/utilsicons.h @@ -31,6 +31,8 @@ namespace Utils { namespace Icons { +QTCREATOR_UTILS_EXPORT extern const Icon HOME; +QTCREATOR_UTILS_EXPORT extern const Icon HOME_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon EDIT_CLEAR; QTCREATOR_UTILS_EXPORT extern const Icon EDIT_CLEAR_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon LOCKED_TOOLBAR; diff --git a/src/plugins/help/help.qrc b/src/plugins/help/help.qrc index d2db8d1a6b5..12c1f0803ee 100644 --- a/src/plugins/help/help.qrc +++ b/src/plugins/help/help.qrc @@ -2,8 +2,6 @@ images/find.png images/book.png - images/home.png - images/home@2x.png images/category_help.png images/mode_help.png images/mode_help@2x.png diff --git a/src/plugins/help/helpwidget.cpp b/src/plugins/help/helpwidget.cpp index b0e6cb3daf7..feb6c3ffa43 100644 --- a/src/plugins/help/helpwidget.cpp +++ b/src/plugins/help/helpwidget.cpp @@ -176,7 +176,7 @@ HelpWidget::HelpWidget(const Core::Context &context, WidgetStyle style, QWidget layout->addWidget(Core::Command::toolButtonWithAppendedShortcut(m_switchToHelp, cmd)); } - m_homeAction = new QAction(Icons::HOME_TOOLBAR.icon(), tr("Home"), this); + m_homeAction = new QAction(Utils::Icons::HOME_TOOLBAR.icon(), tr("Home"), this); cmd = Core::ActionManager::registerAction(m_homeAction, Constants::HELP_HOME, context); connect(m_homeAction, &QAction::triggered, this, &HelpWidget::goHome); layout->addWidget(Core::Command::toolButtonWithAppendedShortcut(m_homeAction, cmd)); diff --git a/src/plugins/projectexplorer/foldernavigationwidget.cpp b/src/plugins/projectexplorer/foldernavigationwidget.cpp index f981545c690..3ba8fecce18 100644 --- a/src/plugins/projectexplorer/foldernavigationwidget.cpp +++ b/src/plugins/projectexplorer/foldernavigationwidget.cpp @@ -25,6 +25,7 @@ #include "foldernavigationwidget.h" #include "projectexplorer.h" +#include "projectexplorericons.h" #include #include @@ -199,6 +200,7 @@ void FolderNavigationWidget::insertRootDirectory( m_rootSelector->setItemData(index, directory.id, ID_ROLE); m_rootSelector->setItemData(index, directory.sortValue, SORT_ROLE); m_rootSelector->setItemData(index, directory.path.toUserOutput(), Qt::ToolTipRole); + m_rootSelector->setItemIcon(index, directory.icon); if (m_rootSelector->currentIndex() == previousIndex) m_rootSelector->setCurrentIndex(index); if (previousIndex < m_rootSelector->count()) @@ -378,11 +380,13 @@ FolderNavigationWidgetFactory::FolderNavigationWidgetFactory() insertRootDirectory({QLatin1String("A.Computer"), 0 /*sortValue*/, FolderNavigationWidget::tr("Computer"), - Utils::FileName()}); + Utils::FileName(), + Icons::DESKTOP_DEVICE_SMALL.icon()}); insertRootDirectory({QLatin1String("A.Home"), 10 /*sortValue*/, FolderNavigationWidget::tr("Home"), - Utils::FileName::fromString(QDir::homePath())}); + Utils::FileName::fromString(QDir::homePath()), + Utils::Icons::HOME.icon()}); updateProjectsDirectoryRoot(); connect(Core::DocumentManager::instance(), &Core::DocumentManager::projectsDirectoryChanged, @@ -465,7 +469,8 @@ void FolderNavigationWidgetFactory::updateProjectsDirectoryRoot() insertRootDirectory({QLatin1String(PROJECTSDIRECTORYROOT_ID), 20 /*sortValue*/, FolderNavigationWidget::tr("Projects"), - Core::DocumentManager::projectsDirectory()}); + Core::DocumentManager::projectsDirectory(), + Utils::Icons::PROJECT.icon()}); } } // namespace Internal diff --git a/src/plugins/projectexplorer/foldernavigationwidget.h b/src/plugins/projectexplorer/foldernavigationwidget.h index 11e2bbc9ec8..55bbf44a278 100644 --- a/src/plugins/projectexplorer/foldernavigationwidget.h +++ b/src/plugins/projectexplorer/foldernavigationwidget.h @@ -28,6 +28,7 @@ #include #include +#include #include namespace Core { class IEditor; } @@ -56,6 +57,7 @@ public: int sortValue; QString displayName; Utils::FileName path; + QIcon icon; }; FolderNavigationWidgetFactory(); diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp index e66e8e7d8b1..b5fddb74ce2 100644 --- a/src/plugins/projectexplorer/session.cpp +++ b/src/plugins/projectexplorer/session.cpp @@ -394,14 +394,19 @@ void SessionManager::addProject(Project *pro) emit m_instance->projectAdded(pro); const auto updateFolderNavigation = [pro] { + const QIcon icon = pro->rootProjectNode() ? pro->rootProjectNode()->icon() : QIcon(); FolderNavigationWidgetFactory::insertRootDirectory({projectFolderId(pro), PROJECT_SORT_VALUE, pro->displayName(), - pro->projectFilePath().parentDir()}); + pro->projectFilePath().parentDir(), + icon}); }; updateFolderNavigation(); configureEditors(pro); - connect(pro, &Project::fileListChanged, [pro](){ configureEditors(pro); }); + connect(pro, &Project::fileListChanged, [pro, updateFolderNavigation]() { + configureEditors(pro); + updateFolderNavigation(); // update icon + }); connect(pro, &Project::displayNameChanged, pro, updateFolderNavigation); } diff --git a/src/shared/help/helpicons.h b/src/shared/help/helpicons.h index f9429a28150..dd4ebce26a5 100644 --- a/src/shared/help/helpicons.h +++ b/src/shared/help/helpicons.h @@ -30,8 +30,6 @@ namespace Help { namespace Icons { -const Utils::Icon HOME_TOOLBAR({ - {QLatin1String(":/help/images/home.png"), Utils::Theme::IconsBaseColor}}); const Utils::Icon MODE_HELP_CLASSIC( QLatin1String(":/help/images/mode_help.png")); const Utils::Icon MODE_HELP_FLAT({ diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 442a2657492..2cfb5042d24 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -4087,7 +4087,7 @@ sodipodi:nodetypes="cccc" /> + id="src/libs/utils/images/home"> Date: Mon, 9 Oct 2017 11:11:22 +0200 Subject: [PATCH 13/27] File System view: Do not open projects on double clicking directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That functionality is available through the context menu. Task-number: QTCREATORBUG-19035 Change-Id: If08fe798f06c013ca5149fad7934d38e357b2342 Reviewed-by: André Hartmann --- .../foldernavigationwidget.cpp | 43 +++++++++++-------- .../projectexplorer/foldernavigationwidget.h | 1 + 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/plugins/projectexplorer/foldernavigationwidget.cpp b/src/plugins/projectexplorer/foldernavigationwidget.cpp index 3ba8fecce18..4a003ba0283 100644 --- a/src/plugins/projectexplorer/foldernavigationwidget.cpp +++ b/src/plugins/projectexplorer/foldernavigationwidget.cpp @@ -294,21 +294,26 @@ int FolderNavigationWidget::bestRootForFile(const Utils::FileName &filePath) void FolderNavigationWidget::openItem(const QModelIndex &index) { - if (!index.isValid()) + QTC_ASSERT(index.isValid(), return); + // signal "activate" is also sent when double-clicking folders + // but we don't want to do anything in that case + if (m_fileSystemModel->isDir(index)) return; const QString path = m_fileSystemModel->filePath(index); - if (m_fileSystemModel->isDir(index)) { - const QFileInfo fi = m_fileSystemModel->fileInfo(index); - if (!fi.isReadable() || !fi.isExecutable()) - return; - // Try to find project files in directory and open those. - const QStringList projectFiles = FolderNavigationWidget::projectFilesInDirectory(path); - if (!projectFiles.isEmpty()) - Core::ICore::instance()->openFiles(projectFiles); - } else { - // Open editor - Core::EditorManager::openEditor(path); - } + Core::EditorManager::openEditor(path); +} + +void FolderNavigationWidget::openProjectsInDirectory(const QModelIndex &index) +{ + QTC_ASSERT(index.isValid() && m_fileSystemModel->isDir(index), return); + const QFileInfo fi = m_fileSystemModel->fileInfo(index); + if (!fi.isReadable() || !fi.isExecutable()) + return; + const QString path = m_fileSystemModel->filePath(index); + // Try to find project files in directory and open those. + const QStringList projectFiles = FolderNavigationWidget::projectFilesInDirectory(path); + if (!projectFiles.isEmpty()) + Core::ICore::instance()->openFiles(projectFiles); } void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev) @@ -317,13 +322,14 @@ void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev) // Open current item const QModelIndex current = m_listView->currentIndex(); const bool hasCurrentItem = current.isValid(); - QAction *actionOpen = nullptr; + QAction *actionOpenFile = nullptr; + QAction *actionOpenProjects = nullptr; if (hasCurrentItem) { const QString fileName = m_fileSystemModel->fileName(current); if (m_fileSystemModel->isDir(current)) - actionOpen = menu.addAction(tr("Open Project in \"%1\"").arg(fileName)); + actionOpenProjects = menu.addAction(tr("Open Project in \"%1\"").arg(fileName)); else - actionOpen = menu.addAction(tr("Open \"%1\"").arg(fileName)); + actionOpenFile = menu.addAction(tr("Open \"%1\"").arg(fileName)); } // we need dummy DocumentModel::Entry with absolute file path in it @@ -339,9 +345,10 @@ void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev) return; ev->accept(); - if (action == actionOpen) { // Handle open file. + if (action == actionOpenFile) openItem(current); - } + else if (action == actionOpenProjects) + openProjectsInDirectory(current); } void FolderNavigationWidget::setHiddenFilesFilter(bool filter) diff --git a/src/plugins/projectexplorer/foldernavigationwidget.h b/src/plugins/projectexplorer/foldernavigationwidget.h index 55bbf44a278..efb72828c33 100644 --- a/src/plugins/projectexplorer/foldernavigationwidget.h +++ b/src/plugins/projectexplorer/foldernavigationwidget.h @@ -107,6 +107,7 @@ private: void setRootDirectory(const Utils::FileName &directory); int bestRootForFile(const Utils::FileName &filePath); void openItem(const QModelIndex &index); + void openProjectsInDirectory(const QModelIndex &index); Utils::NavigationTreeView *m_listView = nullptr; QFileSystemModel *m_fileSystemModel = nullptr; From 240d310a81c2e644ae6f241e2738c17f5a80ef89 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 14 Sep 2017 13:29:46 +0200 Subject: [PATCH 14/27] Android: Re-design android settings Make Android setup related issues visible to user Change-Id: Ib543c41edd9031d2bf12ac2e87b84bf4791781b7 Reviewed-by: BogDan Vatra Reviewed-by: Leena Miettinen --- src/plugins/android/androidsettingswidget.cpp | 432 +++++++++--------- src/plugins/android/androidsettingswidget.h | 21 +- src/plugins/android/androidsettingswidget.ui | 427 +++++++---------- 3 files changed, 397 insertions(+), 483 deletions(-) diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index d5c49623b1b..72c896038f6 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -32,7 +32,9 @@ #include "androidtoolchain.h" #include "androidavdmanager.h" +#include #include +#include #include #include #include @@ -59,6 +61,89 @@ namespace Android { namespace Internal { +namespace { +enum JavaValidation { + JavaPathExistsRow, + JavaJdkValidRow +}; + +enum AndroidValidation { + SdkPathExistsRow, + SdkToolsInstalledRow, + PlatformToolsInstalledRow, + BuildToolsInstalledRow, + PlatformSdkInstalledRow, + NdkPathExistsRow, + NdkDirStructureRow, + NdkinstallDirOkRow +}; +} + +class SummaryWidget : public QWidget +{ + class RowData { + public: + QLabel *m_iconLabel = nullptr; + Utils::ElidingLabel *m_textLabel = nullptr; + bool m_valid = false; + }; + +public: + SummaryWidget(const QMap &validationPoints, const QString &validText, + const QString &invalidText, Utils::DetailsWidget *detailsWidget) : + QWidget(detailsWidget), + m_validText(validText), + m_invalidText(invalidText), + m_detailsWidget(detailsWidget) + { + QTC_CHECK(m_detailsWidget); + auto layout = new QGridLayout(this); + layout->setMargin(12); + int row = 0; + for (auto itr = validationPoints.cbegin(); itr != validationPoints.cend(); ++itr) { + RowData data; + data.m_iconLabel = new QLabel(this); + layout->addWidget(data.m_iconLabel, row, 0, 1, 1); + data.m_textLabel = new Utils::ElidingLabel(itr.value(), this); + data.m_textLabel->setElideMode(Qt::ElideRight); + data.m_textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + layout->addWidget(data.m_textLabel, row, 1, 1, 1); + m_validationData[itr.key()] = data; + setPointValid(itr.key(), true); + ++row; + } + } + + void setPointValid(int key, bool valid) + { + if (!m_validationData.contains(key)) + return; + RowData& data = m_validationData[key]; + 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); + } + + bool allRowsOk() const + { + for (auto itr = m_validationData.cbegin(); itr != m_validationData.cend(); ++itr) { + if (!itr.value().m_valid) + return false; + } + return true; + } + +private: + QString m_validText; + QString m_invalidText; + Utils::DetailsWidget *m_detailsWidget = nullptr; + QMap m_validationData; +}; + void AvdModel::setAvdList(const AndroidDeviceInfoList &list) { beginResetModel(); @@ -125,15 +210,39 @@ int AvdModel::columnCount(const QModelIndex &/*parent*/) const AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent) : QWidget(parent), - m_sdkState(NotSet), - m_ndkState(NotSet), - m_javaState(NotSet), m_ui(new Ui_AndroidSettingsWidget), m_androidConfig(AndroidConfigurations::currentConfig()), m_avdManager(new AndroidAvdManager(m_androidConfig)) { m_ui->setupUi(this); + QMap javaValidationPoints; + javaValidationPoints[JavaPathExistsRow] = tr("JDK path exists."); + javaValidationPoints[JavaJdkValidRow] = tr("JDK path is a valid JDK root folder."); + auto javaSummary = new SummaryWidget(javaValidationPoints, tr("Java Settings are OK."), + tr("Java settings have errors."), m_ui->javaDetailsWidget); + m_ui->javaDetailsWidget->setWidget(javaSummary); + + QMap androidValidationPoints; + androidValidationPoints[SdkPathExistsRow] = tr("Android SDK path exists."); + androidValidationPoints[SdkToolsInstalledRow] = tr("SDK tools installed."); + androidValidationPoints[PlatformToolsInstalledRow] = tr("Platform tools installed."); + androidValidationPoints[BuildToolsInstalledRow] = tr("Build tools installed."); + androidValidationPoints[PlatformSdkInstalledRow] = tr("Platform SDK installed."); + androidValidationPoints[NdkPathExistsRow] = tr("Android NDK path exists."); + androidValidationPoints[NdkDirStructureRow] = tr("Android NDK directory structure is correct."); + androidValidationPoints[NdkinstallDirOkRow] = tr("Android NDK installed into a path without " + "spaces."); + auto androidSummary = new SummaryWidget(androidValidationPoints, tr("Android settings are OK."), + tr("Android settings have errors."), + m_ui->androidDetailsWidget); + m_ui->androidDetailsWidget->setWidget(androidSummary); + + auto kitsDetailsLabel = new QLabel(m_ui->kitWarningDetails); + kitsDetailsLabel->setWordWrap(true); + m_ui->kitWarningDetails->setWidget(kitsDetailsLabel); + m_ui->kitWarningDetails->setIcon(Utils::Icons::WARNING.icon()); + m_ui->SDKLocationPathChooser->setFileName(m_androidConfig.sdkLocation()); m_ui->SDKLocationPathChooser->setPromptDialogTitle(tr("Select Android SDK folder")); m_ui->NDKLocationPathChooser->setFileName(m_androidConfig.ndkLocation()); @@ -149,29 +258,16 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent) m_ui->downloadOpenJDKToolButton->setVisible(!Utils::HostOsInfo::isLinuxHost()); - const QPixmap warningPixmap = Utils::Icons::WARNING.pixmap(); - m_ui->jdkWarningIconLabel->setPixmap(warningPixmap); - m_ui->kitWarningIconLabel->setPixmap(warningPixmap); - - const QPixmap errorPixmap = Utils::Icons::CRITICAL.pixmap(); - m_ui->sdkWarningIconLabel->setPixmap(errorPixmap); - m_ui->ndkWarningIconLabel->setPixmap(errorPixmap); - connect(&m_virtualDevicesWatcher, &QFutureWatcherBase::finished, this, &AndroidSettingsWidget::updateAvds); - - check(All); - applyToUi(All); - connect(&m_futureWatcher, &QFutureWatcherBase::finished, this, &AndroidSettingsWidget::avdAdded); - connect(m_ui->NDKLocationPathChooser, &Utils::PathChooser::rawPathChanged, - this, &AndroidSettingsWidget::ndkLocationEditingFinished); + this, &AndroidSettingsWidget::validateNdk); connect(m_ui->SDKLocationPathChooser, &Utils::PathChooser::rawPathChanged, - this, &AndroidSettingsWidget::sdkLocationEditingFinished); + this, &AndroidSettingsWidget::validateSdk); connect(m_ui->OpenJDKLocationPathChooser, &Utils::PathChooser::rawPathChanged, - this, &AndroidSettingsWidget::openJDKLocationEditingFinished); + this, &AndroidSettingsWidget::validateJdk); connect(m_ui->AVDAddPushButton, &QAbstractButton::clicked, this, &AndroidSettingsWidget::addAVD); connect(m_ui->AVDRemovePushButton, &QAbstractButton::clicked, @@ -194,6 +290,10 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent) this, &AndroidSettingsWidget::openNDKDownloadUrl); connect(m_ui->downloadOpenJDKToolButton, &QAbstractButton::clicked, this, &AndroidSettingsWidget::openOpenJDKDownloadUrl); + + validateJdk(); + validateNdk(); + validateSdk(); } AndroidSettingsWidget::~AndroidSettingsWidget() @@ -202,144 +302,6 @@ AndroidSettingsWidget::~AndroidSettingsWidget() m_futureWatcher.waitForFinished(); } -void AndroidSettingsWidget::check(AndroidSettingsWidget::Mode mode) -{ - if (mode & Sdk) { - m_sdkState = verifySdkInstallation(&m_sdkInstallationError) ? Okay : Error; - } - - if (mode & Ndk) { - m_ndkState = Okay; - Utils::FileName platformPath = m_androidConfig.ndkLocation(); - Utils::FileName toolChainPath = m_androidConfig.ndkLocation(); - Utils::FileName sourcesPath = m_androidConfig.ndkLocation(); - if (m_androidConfig.ndkLocation().isEmpty()) { - m_ndkState = NotSet; - } else if (!platformPath.appendPath(QLatin1String("platforms")).exists() - || !toolChainPath.appendPath(QLatin1String("toolchains")).exists() - || !sourcesPath.appendPath(QLatin1String("sources/cxx-stl")).exists()) { - m_ndkState = Error; - m_ndkErrorMessage = tr("\"%1\" does not seem to be an Android NDK top folder.") - .arg(m_androidConfig.ndkLocation().toUserOutput()); - } else if (platformPath.toString().contains(QLatin1Char(' '))) { - m_ndkState = Error; - m_ndkErrorMessage = tr("The Android NDK cannot be installed into a path with spaces."); - } else { - QList compilerPaths - = AndroidToolChainFactory::toolchainPathsForNdk(m_androidConfig.ndkLocation()); - m_ndkCompilerCount = compilerPaths.count(); - - // See if we have qt versions for those toolchains - QSet toolchainsForAbi; - foreach (const AndroidToolChainFactory::AndroidToolChainInformation &ati, compilerPaths) { - if (ati.language == Core::Id(ProjectExplorer::Constants::CXX_LANGUAGE_ID)) - toolchainsForAbi.insert(ati.abi); - } - - const QList androidQts - = QtSupport::QtVersionManager::versions([](const QtSupport::BaseQtVersion *v) { - return v->type() == QLatin1String(Constants::ANDROIDQT) && !v->qtAbis().isEmpty(); - }); - QSet qtVersionsForAbi; - foreach (QtSupport::BaseQtVersion *qtVersion, androidQts) - qtVersionsForAbi.insert(qtVersion->qtAbis().first()); - - QSet missingQtArchs = toolchainsForAbi.subtract(qtVersionsForAbi); - if (missingQtArchs.isEmpty()) { - m_ndkMissingQtArchs.clear(); - } else { - if (missingQtArchs.count() == 1) { - m_ndkMissingQtArchs = tr("Qt version for architecture %1 is missing.\n" - "To add the Qt version, select Options > Build & Run > Qt Versions.") - .arg((*missingQtArchs.constBegin()).toString()); - } else { - m_ndkMissingQtArchs = tr("Qt versions for %n architectures are missing.\n" - "To add the Qt versions, select Options > Build & Run > Qt Versions.", - nullptr, missingQtArchs.size()); - } - } - } - } - - if (mode & Java) { - m_javaState = Okay; - if (m_androidConfig.openJDKLocation().isEmpty()) { - m_javaState = NotSet; - } else { - Utils::FileName bin = m_androidConfig.openJDKLocation(); - bin.appendPath(QLatin1String("bin/javac" QTC_HOST_EXE_SUFFIX)); - if (!m_androidConfig.openJDKLocation().exists() || !bin.exists()) - m_javaState = Error; - } - } -} - -void AndroidSettingsWidget::applyToUi(AndroidSettingsWidget::Mode mode) -{ - if (mode & Sdk) { - if (m_sdkState == Error) { - m_ui->sdkWarningIconLabel->setVisible(true); - m_ui->sdkWarningLabel->setVisible(true); - m_ui->sdkWarningLabel->setText(m_sdkInstallationError); - } else { - m_ui->sdkWarningIconLabel->setVisible(false); - m_ui->sdkWarningLabel->setVisible(false); - } - } - - if (mode & Ndk) { - if (m_ndkState == NotSet) { - m_ui->ndkWarningIconLabel->setVisible(false); - m_ui->toolchainFoundLabel->setVisible(false); - m_ui->kitWarningIconLabel->setVisible(false); - m_ui->kitWarningLabel->setVisible(false); - } else if (m_ndkState == Error) { - m_ui->toolchainFoundLabel->setText(m_ndkErrorMessage); - m_ui->toolchainFoundLabel->setVisible(true); - m_ui->ndkWarningIconLabel->setVisible(true); - m_ui->kitWarningIconLabel->setVisible(false); - m_ui->kitWarningLabel->setVisible(false); - } else { - if (m_ndkCompilerCount > 0) { - m_ui->ndkWarningIconLabel->setVisible(false); - m_ui->toolchainFoundLabel->setText(tr("Found %n toolchains for this NDK.", 0, m_ndkCompilerCount)); - m_ui->toolchainFoundLabel->setVisible(true); - } else { - m_ui->ndkWarningIconLabel->setVisible(false); - m_ui->toolchainFoundLabel->setVisible(false); - } - - if (m_ndkMissingQtArchs.isEmpty()) { - m_ui->kitWarningIconLabel->setVisible(false); - m_ui->kitWarningLabel->setVisible(false); - } else { - m_ui->kitWarningIconLabel->setVisible(true); - m_ui->kitWarningLabel->setVisible(true); - m_ui->kitWarningLabel->setText(m_ndkMissingQtArchs); - } - } - } - - if (mode & Java) { - Utils::FileName location = m_androidConfig.openJDKLocation(); - bool error = m_javaState == Error; - m_ui->jdkWarningIconLabel->setVisible(error); - m_ui->jdkWarningLabel->setVisible(error); - if (error) - m_ui->jdkWarningLabel->setText(tr("\"%1\" does not seem to be a JDK folder.").arg(location.toUserOutput())); - } - - if (mode & Sdk || mode & Java) { - if (m_sdkState == Okay && m_javaState == Okay) { - m_ui->AVDManagerFrame->setEnabled(true); - } else { - m_ui->AVDManagerFrame->setEnabled(false); - } - - startUpdateAvd(); - } -} - void AndroidSettingsWidget::disableAvdControls() { m_ui->AVDAddPushButton->setEnabled(false); @@ -371,75 +333,65 @@ void AndroidSettingsWidget::updateAvds() enableAvdControls(); } -bool AndroidSettingsWidget::verifySdkInstallation(QString *errorDetails) const -{ - if (m_androidConfig.sdkLocation().isEmpty()) { - if (errorDetails) - *errorDetails = tr("Android SDK path not set."); - return false; - } - - if (!m_androidConfig.sdkLocation().exists()) { - if (errorDetails) - *errorDetails = tr("Android SDK path does not exist."); - return false; - } - - if (m_androidConfig.sdkToolsVersion().isNull()) { - if (errorDetails) - *errorDetails = tr("The SDK path does not seem to be a valid Android SDK top folder."); - return false; - } - - QStringList missingComponents; - if (!m_androidConfig.adbToolPath().exists()) - missingComponents << "Platform Tools"; - - if (m_androidConfig.buildToolsVersion().isNull()) - missingComponents << "Build Tools"; - - if (m_androidConfig.sdkTargets().isEmpty()) - missingComponents << "Platform SDK"; - - if (!missingComponents.isEmpty() && errorDetails) { - *errorDetails = tr("Android SDK components missing (%1).\nUse Android SDK Manager to " - "manage SDK components.").arg(missingComponents.join(", ")); - } - - return missingComponents.isEmpty(); -} - void AndroidSettingsWidget::saveSettings() { - sdkLocationEditingFinished(); - ndkLocationEditingFinished(); - openJDKLocationEditingFinished(); - dataPartitionSizeEditingFinished(); AndroidConfigurations::setConfig(m_androidConfig); } -void AndroidSettingsWidget::sdkLocationEditingFinished() +void AndroidSettingsWidget::validateJdk() { - m_androidConfig.setSdkLocation(Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath())); + auto javaPath = Utils::FileName::fromUserInput(m_ui->OpenJDKLocationPathChooser->rawPath()); + m_androidConfig.setOpenJDKLocation(javaPath); + bool jdkPathExists = m_androidConfig.openJDKLocation().exists(); + auto summaryWidget = static_cast(m_ui->javaDetailsWidget->widget()); + summaryWidget->setPointValid(JavaPathExistsRow, jdkPathExists); - check(Sdk); - applyToUi(Sdk); + Utils::FileName bin = m_androidConfig.openJDKLocation(); + bin.appendPath(QLatin1String("bin/javac" QTC_HOST_EXE_SUFFIX)); + summaryWidget->setPointValid(JavaJdkValidRow, jdkPathExists && bin.exists()); + updateUI(); } -void AndroidSettingsWidget::ndkLocationEditingFinished() +void AndroidSettingsWidget::validateNdk() { - m_androidConfig.setNdkLocation(Utils::FileName::fromUserInput(m_ui->NDKLocationPathChooser->rawPath())); + auto ndkPath = Utils::FileName::fromUserInput(m_ui->NDKLocationPathChooser->rawPath()); + m_androidConfig.setNdkLocation(ndkPath); - check(Ndk); - applyToUi(Ndk); + auto summaryWidget = static_cast(m_ui->androidDetailsWidget->widget()); + summaryWidget->setPointValid(NdkPathExistsRow, m_androidConfig.ndkLocation().exists()); + + Utils::FileName ndkPlatformsDir(ndkPath); + ndkPlatformsDir.appendPath("platforms"); + Utils::FileName ndkToolChainsDir(ndkPath); + ndkToolChainsDir.appendPath("toolchains"); + Utils::FileName ndkSourcesDir(ndkPath); + ndkSourcesDir.appendPath("sources/cxx-stl"); + summaryWidget->setPointValid(NdkDirStructureRow, + ndkPlatformsDir.exists() + && ndkToolChainsDir.exists() + && ndkSourcesDir.exists()); + summaryWidget->setPointValid(NdkinstallDirOkRow, + ndkPlatformsDir.exists() && + !ndkPlatformsDir.toString().contains(' ')); + updateUI(); } -void AndroidSettingsWidget::openJDKLocationEditingFinished() +void AndroidSettingsWidget::validateSdk() { - m_androidConfig.setOpenJDKLocation(Utils::FileName::fromUserInput(m_ui->OpenJDKLocationPathChooser->rawPath())); + auto sdkPath = Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath()); + m_androidConfig.setSdkLocation(sdkPath); - check(Java); - applyToUi(Java); + auto summaryWidget = static_cast(m_ui->androidDetailsWidget->widget()); + summaryWidget->setPointValid(SdkPathExistsRow, m_androidConfig.sdkLocation().exists()); + summaryWidget->setPointValid(SdkToolsInstalledRow, + !m_androidConfig.sdkToolsVersion().isNull()); + summaryWidget->setPointValid(PlatformToolsInstalledRow, + m_androidConfig.adbToolPath().exists()); + summaryWidget->setPointValid(BuildToolsInstalledRow, + !m_androidConfig.buildToolsVersion().isNull()); + summaryWidget->setPointValid(PlatformSdkInstalledRow, + !m_androidConfig.sdkTargets().isEmpty()); + updateUI(); } void AndroidSettingsWidget::openSDKDownloadUrl() @@ -520,6 +472,64 @@ void AndroidSettingsWidget::createKitToggled() m_androidConfig.setAutomaticKitCreation(m_ui->CreateKitCheckBox->isChecked()); } +void AndroidSettingsWidget::checkMissingQtVersion() +{ + auto summaryWidget = static_cast(m_ui->androidDetailsWidget->widget()); + if (!summaryWidget->allRowsOk()) { + m_ui->kitWarningDetails->setVisible(false); + m_ui->kitWarningDetails->setState(Utils::DetailsWidget::Collapsed); + return; + } + + QList compilerPaths + = AndroidToolChainFactory::toolchainPathsForNdk(m_androidConfig.ndkLocation()); + + // See if we have qt versions for those toolchains + QSet toolchainsForAbi; + foreach (const AndroidToolChainFactory::AndroidToolChainInformation &ati, compilerPaths) { + if (ati.language == Core::Id(ProjectExplorer::Constants::CXX_LANGUAGE_ID)) + toolchainsForAbi.insert(ati.abi); + } + + const QList androidQts + = QtSupport::QtVersionManager::versions([](const QtSupport::BaseQtVersion *v) { + return v->type() == QLatin1String(Constants::ANDROIDQT) && !v->qtAbis().isEmpty(); + }); + QSet qtVersionsForAbi; + foreach (QtSupport::BaseQtVersion *qtVersion, androidQts) + qtVersionsForAbi.insert(qtVersion->qtAbis().first()); + + QSet missingQtArchs = toolchainsForAbi.subtract(qtVersionsForAbi); + bool isArchMissing = !missingQtArchs.isEmpty(); + m_ui->kitWarningDetails->setVisible(isArchMissing); + if (isArchMissing) { + m_ui->kitWarningDetails->setSummaryText(tr("Cannot create kits for all architectures.")); + QLabel *detailsLabel = static_cast(m_ui->kitWarningDetails->widget()); + QStringList archNames; + for (auto abi : missingQtArchs) + archNames << abi.toString(); + detailsLabel->setText(tr("Qt versions are missing for the following architectures:\n%1" + "\n\nTo add the Qt version, select Options > Build & Run > Qt" + " Versions.").arg(archNames.join(", "))); + } +} + +void AndroidSettingsWidget::updateUI() +{ + auto javaSummaryWidget = static_cast(m_ui->javaDetailsWidget->widget()); + auto androidSummaryWidget = static_cast(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); + startUpdateAvd(); + checkMissingQtVersion(); +} + void AndroidSettingsWidget::manageAVD() { if (m_avdManager->avdManagerUiToolAvailable()) { diff --git a/src/plugins/android/androidsettingswidget.h b/src/plugins/android/androidsettingswidget.h index 32127543657..41c6a32650b 100644 --- a/src/plugins/android/androidsettingswidget.h +++ b/src/plugins/android/androidsettingswidget.h @@ -73,9 +73,9 @@ public: void saveSettings(); private: - void sdkLocationEditingFinished(); - void ndkLocationEditingFinished(); - void openJDKLocationEditingFinished(); + void validateJdk(); + void validateNdk(); + void validateSdk(); void openSDKDownloadUrl(); void openNDKDownloadUrl(); void openOpenJDKDownloadUrl(); @@ -88,26 +88,15 @@ private: void manageAVD(); void createKitToggled(); + void checkMissingQtVersion(); + void updateUI(); void updateAvds(); private: - enum Mode { Sdk = 1, Ndk = 2, Java = 4, All = Sdk | Ndk | Java }; - enum State { NotSet = 0, Okay = 1, Error = 2 }; - bool verifySdkInstallation(QString *errorDetails = nullptr) const; - void check(Mode mode); - void applyToUi(Mode mode); void startUpdateAvd(); void enableAvdControls(); void disableAvdControls(); - State m_sdkState; - QString m_sdkInstallationError; - State m_ndkState; - QString m_ndkErrorMessage; - int m_ndkCompilerCount; - QString m_ndkMissingQtArchs; - State m_javaState; - Ui_AndroidSettingsWidget *m_ui; AndroidConfig m_androidConfig; AvdModel m_AVDModel; diff --git a/src/plugins/android/androidsettingswidget.ui b/src/plugins/android/androidsettingswidget.ui index 0d8bcb774d5..878c1d95ee7 100644 --- a/src/plugins/android/androidsettingswidget.ui +++ b/src/plugins/android/androidsettingswidget.ui @@ -6,15 +6,175 @@ 0 0 - 843 - 625 + 938 + 728 Android Configuration - - + + + + + + 0 + 0 + + + + Java Settings + + + + + + + 0 + 0 + + + + + + + + Download JDK + + + + :/android/images/download.png:/android/images/download.png + + + + + + + + 0 + 0 + + + + JDK location: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + + + + + Android Settings + + + + + + + + + + 0 + 0 + + + + + + + + Download Android NDK + + + + :/android/images/download.png:/android/images/download.png + + + + + + + + 0 + 0 + + + + Android NDK location: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Android SDK location: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Download Android SDK + + + + :/android/images/download.png:/android/images/download.png + + + + + + + + + + + + + + 0 + 0 + + + + Automatically create kits for Android tool chains + + + true + + + + + + + QFrame::StyledPanel @@ -133,264 +293,19 @@ - - - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - - - - true - - - - - - - - - Download Android NDK - - - - :/android/images/download.png:/android/images/download.png - - - - - - - - 0 - 0 - - - - Android SDK location: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Download JDK - - - - :/android/images/download.png:/android/images/download.png - - - - - - - Download Android SDK - - - - :/android/images/download.png:/android/images/download.png - - - - - - - 2 - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - - - - true - - - - - - - - - 2 - - - 0 - - - - - - 0 - 0 - - - - Automatically create kits for Android tool chains - - - true - - - - - - - - - - 0 - 0 - - - - Android NDK location: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 2 - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - - - - true - - - - - - - - - - 0 - 0 - - - - JDK location: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Utils::PathChooser QWidget -
utils/pathchooser.h
+
utils/pathchooser.h
+ 1 +
+ + Utils::DetailsWidget + QWidget +
utils/detailswidget.h
1
From ec4fe5f04f4c961615bb27fa4e7d9638a3c6c971 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Fri, 18 Aug 2017 08:22:34 +0200 Subject: [PATCH 15/27] Android: Refactor Android SDK packages Introduce a hierarchy for Android SDK packages and refactor the code accordingly. This is ground work for sdk management and automatic android setup Task-number: QTCREATORBUG-18978 Change-Id: Idef545e3b3a8e33e920be52b26094fb8046afcd3 Reviewed-by: BogDan Vatra --- src/plugins/android/android.pro | 6 +- src/plugins/android/androidavdmanager.cpp | 25 +- src/plugins/android/androidavdmanager.h | 2 +- src/plugins/android/androidbuildapkstep.cpp | 10 +- src/plugins/android/androidbuildapkwidget.cpp | 8 +- src/plugins/android/androidconfigurations.cpp | 80 +--- src/plugins/android/androidconfigurations.h | 58 +-- src/plugins/android/androiddevicedialog.cpp | 8 +- src/plugins/android/androiddevicedialog.h | 2 +- src/plugins/android/androidmanager.cpp | 4 +- src/plugins/android/androidsdkmanager.cpp | 411 +++++++++++++----- src/plugins/android/androidsdkmanager.h | 34 +- src/plugins/android/androidsdkpackage.cpp | 193 ++++++++ src/plugins/android/androidsdkpackage.h | 150 +++++++ src/plugins/android/androidsettingswidget.cpp | 13 +- src/plugins/android/androidsettingswidget.h | 3 +- src/plugins/android/androidtoolmanager.cpp | 94 ++-- src/plugins/android/androidtoolmanager.h | 6 +- src/plugins/android/avddialog.cpp | 50 ++- src/plugins/android/avddialog.h | 14 +- 20 files changed, 854 insertions(+), 317 deletions(-) create mode 100644 src/plugins/android/androidsdkpackage.cpp create mode 100644 src/plugins/android/androidsdkpackage.h diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index 67cc03f6ca4..6cd59c4b705 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -50,7 +50,8 @@ HEADERS += \ androidsdkmanager.h \ androidavdmanager.h \ androidrunconfigurationwidget.h \ - adbcommandswidget.h + adbcommandswidget.h \ + androidsdkpackage.h SOURCES += \ androidconfigurations.cpp \ @@ -94,7 +95,8 @@ SOURCES += \ androidsdkmanager.cpp \ androidavdmanager.cpp \ androidrunconfigurationwidget.cpp \ - adbcommandswidget.cpp + adbcommandswidget.cpp \ + androidsdkpackage.cpp FORMS += \ androidsettingswidget.ui \ diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index 028fe2c7974..f4cf488616f 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -101,37 +101,37 @@ static bool checkForTimeout(const chrono::steady_clock::time_point &start, return timedOut; } -static AndroidConfig::CreateAvdInfo createAvdCommand(const AndroidConfig config, - const AndroidConfig::CreateAvdInfo &info) +static CreateAvdInfo createAvdCommand(const AndroidConfig config, const CreateAvdInfo &info) { - AndroidConfig::CreateAvdInfo result = info; + CreateAvdInfo result = info; if (!result.isValid()) { qCDebug(avdManagerLog) << "AVD Create failed. Invalid CreateAvdInfo" << result.name - << result.target.name << result.target.apiLevel; + << result.sdkPlatform->displayText() << result.sdkPlatform->apiLevel(); result.error = QApplication::translate("AndroidAvdManager", "Cannot create AVD. Invalid input."); return result; } - QStringList arguments({"create", "avd", "-k", result.target.package, "-n", result.name}); + QStringList arguments({"create", "avd", "-k", result.sdkPlatform->sdkStylePath(), "-n", result.name}); if (!result.abi.isEmpty()) { - SystemImage image = Utils::findOrDefault(result.target.systemImages, + SystemImage *image = Utils::findOrDefault(result.sdkPlatform->systemImages(), Utils::equal(&SystemImage::abiName, result.abi)); - if (image.isValid()) { - arguments << "-k" << image.package; + if (image && image->isValid()) { + arguments << "-k" << image->sdkStylePath(); } else { + QString name = result.sdkPlatform->displayText(); qCDebug(avdManagerLog) << "AVD Create failed. Cannot find system image for the platform" - << result.abi << result.target.name; + << result.abi << name; result.error = QApplication::translate("AndroidAvdManager", "Cannot create AVD. Cannot find system image for " - "the ABI %1(%2).").arg(result.abi).arg(result.target.name); + "the ABI %1(%2).").arg(result.abi).arg(name); return result; } } else { - arguments << "-k" << result.target.package; + arguments << "-k" << result.sdkPlatform->sdkStylePath(); } if (result.sdcardSize > 0) @@ -234,8 +234,7 @@ void AndroidAvdManager::launchAvdManagerUiTool() const } } -QFuture -AndroidAvdManager::createAvd(AndroidConfig::CreateAvdInfo info) const +QFuture AndroidAvdManager::createAvd(CreateAvdInfo info) const { if (m_config.sdkToolsVersion() < avdManagerIntroVersion) return m_androidTool->createAvd(info); diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h index 4e8633efda9..5e079444e3d 100644 --- a/src/plugins/android/androidavdmanager.h +++ b/src/plugins/android/androidavdmanager.h @@ -42,7 +42,7 @@ public: bool avdManagerUiToolAvailable() const; void launchAvdManagerUiTool() const; - QFuture createAvd(AndroidConfig::CreateAvdInfo info) const; + QFuture createAvd(CreateAvdInfo info) const; bool removeAvd(const QString &name) const; QFuture avdList() const; diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index 8ed1316a253..7972e959ab0 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -29,6 +29,7 @@ #include "androidconfigurations.h" #include "androidconstants.h" #include "androidmanager.h" +#include "androidsdkmanager.h" #include "androidqtsupport.h" #include "certificatesmodel.h" @@ -92,7 +93,8 @@ private: AndroidBuildApkStep::AndroidBuildApkStep(ProjectExplorer::BuildStepList *parent, const Core::Id id) : ProjectExplorer::AbstractProcessStep(parent, id), - m_buildTargetSdk(AndroidConfig::apiLevelNameFor(AndroidConfigurations::currentConfig().highestAndroidSdk())) + m_buildTargetSdk(AndroidConfig::apiLevelNameFor(AndroidConfigurations:: + sdkManager()->latestAndroidSdkPlatform())) { //: AndroidBuildApkStep default display name setDefaultDisplayName(tr("Build Android APK")); @@ -236,8 +238,10 @@ bool AndroidBuildApkStep::fromMap(const QVariantMap &map) m_keystorePath = Utils::FileName::fromString(map.value(KeystoreLocationKey).toString()); m_signPackage = false; // don't restore this m_buildTargetSdk = map.value(BuildTargetSdkKey).toString(); - if (m_buildTargetSdk.isEmpty()) - m_buildTargetSdk = AndroidConfig::apiLevelNameFor(AndroidConfigurations::currentConfig().highestAndroidSdk()); + if (m_buildTargetSdk.isEmpty()) { + m_buildTargetSdk = AndroidConfig::apiLevelNameFor(AndroidConfigurations:: + sdkManager()->latestAndroidSdkPlatform()); + } m_verbose = map.value(VerboseOutputKey).toBool(); return ProjectExplorer::BuildStep::fromMap(map); } diff --git a/src/plugins/android/androidbuildapkwidget.cpp b/src/plugins/android/androidbuildapkwidget.cpp index 73c1ad531c3..6b386d9a7bd 100644 --- a/src/plugins/android/androidbuildapkwidget.cpp +++ b/src/plugins/android/androidbuildapkwidget.cpp @@ -29,6 +29,7 @@ #include "androidconfigurations.h" #include "androidcreatekeystorecertificate.h" #include "androidmanager.h" +#include "androidsdkmanager.h" #include "ui_androidbuildapkwidget.h" #include @@ -47,6 +48,8 @@ using namespace Android; using namespace Internal; +const int minApiSupported = 9; + AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step) : ProjectExplorer::BuildStepConfigWidget(), m_ui(new Ui::AndroidBuildApkWidget), @@ -55,9 +58,8 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step) m_ui->setupUi(this); // Target sdk combobox - int minApiLevel = 9; - const AndroidConfig &config = AndroidConfigurations::currentConfig(); - QStringList targets = AndroidConfig::apiLevelNamesFor(config.sdkTargets(minApiLevel)); + QStringList targets = AndroidConfig::apiLevelNamesFor(AndroidConfigurations::sdkManager()-> + filteredSdkPlatforms(minApiSupported)); targets.removeDuplicates(); m_ui->targetSDKComboBox->addItems(targets); m_ui->targetSDKComboBox->setCurrentIndex(targets.indexOf(AndroidManager::buildTargetSDK(step->target()))); diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index b52810e93f0..12a2e9e991d 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -272,7 +272,6 @@ void AndroidConfig::load(const QSettings &settings) m_makeExtraSearchDirectories << extraDirectory; // persistent settings } - m_availableSdkPlatformsUpToDate = false; m_NdkInformationUpToDate = false; } @@ -333,40 +332,15 @@ void AndroidConfig::updateNdkInformation() const m_NdkInformationUpToDate = true; } -void AndroidConfig::updateAvailableSdkPlatforms() const -{ - if (m_availableSdkPlatformsUpToDate) - return; - - m_availableSdkPlatforms.clear(); - AndroidSdkManager sdkManager(*this); - bool success = false; - m_availableSdkPlatforms = sdkManager.availableSdkPlatforms(&success); - if (success) - m_availableSdkPlatformsUpToDate = true; -} - -QStringList AndroidConfig::apiLevelNamesFor(const QList &platforms) +QStringList AndroidConfig::apiLevelNamesFor(const SdkPlatformList &platforms) { return Utils::transform(platforms, AndroidConfig::apiLevelNameFor); } -QString AndroidConfig::apiLevelNameFor(const SdkPlatform &platform) +QString AndroidConfig::apiLevelNameFor(const SdkPlatform *platform) { - return platform.apiLevel > 0 ? QString("android-%1").arg(platform.apiLevel) : ""; -} - -QList AndroidConfig::sdkTargets(int minApiLevel) const -{ - updateAvailableSdkPlatforms(); - QList result; - for (int i = 0; i < m_availableSdkPlatforms.size(); ++i) { - if (m_availableSdkPlatforms.at(i).apiLevel >= minApiLevel) - result << m_availableSdkPlatforms.at(i); - else - break; - } - return result; + return platform && platform->apiLevel() > 0 ? + QString("android-%1").arg(platform->apiLevel()) : ""; } FileName AndroidConfig::adbToolPath() const @@ -523,20 +497,6 @@ QVector AndroidConfig::connectedDevices(const QString &adbToo return devices; } -AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent, int minApiLevel, QString targetArch) const -{ - CreateAvdInfo result; - AvdDialog d(minApiLevel, targetArch, this, parent); - if (d.exec() != QDialog::Accepted || !d.isValid()) - return result; - - result.target = d.target(); - result.name = d.name(); - result.abi = d.abi(); - result.sdcardSize = d.sdcardSize(); - return result; -} - bool AndroidConfig::isConnected(const QString &serialNumber) const { QVector devices = connectedDevices(); @@ -716,14 +676,6 @@ QStringList AndroidConfig::getAbis(const QString &adbToolPath, const QString &de return result; } -SdkPlatform AndroidConfig::highestAndroidSdk() const -{ - updateAvailableSdkPlatforms(); - if (m_availableSdkPlatforms.isEmpty()) - return SdkPlatform(); - return m_availableSdkPlatforms.first(); -} - QString AndroidConfig::bestNdkPlatformMatch(int target) const { target = std::max(9, target); @@ -743,7 +695,6 @@ FileName AndroidConfig::sdkLocation() const void AndroidConfig::setSdkLocation(const FileName &sdkLocation) { m_sdkLocation = sdkLocation; - m_availableSdkPlatformsUpToDate = false; } QVersionNumber AndroidConfig::sdkToolsVersion() const @@ -836,7 +787,6 @@ FileName AndroidConfig::openJDKLocation() const void AndroidConfig::setOpenJDKLocation(const FileName &openJDKLocation) { m_openJDKLocation = openJDKLocation; - m_availableSdkPlatformsUpToDate = false; } FileName AndroidConfig::keystoreLocation() const @@ -1129,6 +1079,11 @@ const AndroidConfig &AndroidConfigurations::currentConfig() return m_instance->m_config; // ensure that m_instance is initialized } +AndroidSdkManager *AndroidConfigurations::sdkManager() +{ + return m_instance->m_sdkManager.get(); +} + AndroidConfigurations *AndroidConfigurations::instance() { return m_instance; @@ -1143,7 +1098,8 @@ void AndroidConfigurations::save() } AndroidConfigurations::AndroidConfigurations(QObject *parent) - : QObject(parent) + : QObject(parent), + m_sdkManager(new AndroidSdkManager(m_config)) { load(); @@ -1155,6 +1111,11 @@ AndroidConfigurations::AndroidConfigurations(QObject *parent) m_instance = this; } +AndroidConfigurations::~AndroidConfigurations() +{ + +} + static FileName javaHomeForJavac(const FileName &location) { QFileInfo fileInfo = location.toFileInfo(); @@ -1265,13 +1226,4 @@ void AndroidConfigurations::updateAndroidDevice() AndroidConfigurations *AndroidConfigurations::m_instance = 0; -bool SdkPlatform::operator <(const SdkPlatform &other) const -{ - if (apiLevel != other.apiLevel) - return apiLevel > other.apiLevel; - if (name != other.name) - return name < other.name; - return false; -} - } // namespace Android diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 75b6dd16abc..9fc349d267c 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -26,7 +26,7 @@ #pragma once #include "android_global.h" - +#include "androidsdkpackage.h" #include #include @@ -52,7 +52,9 @@ class Project; namespace Utils { class Environment; } namespace Android { + class AndroidPlugin; +namespace Internal { class AndroidSdkManager; } class AndroidDeviceInfo { @@ -74,31 +76,16 @@ public: }; using AndroidDeviceInfoList = QList; -//! Defines an Android system image. -class SystemImage +class CreateAvdInfo { public: - bool isValid() const { return (apiLevel != -1) && !abiName.isEmpty(); } - int apiLevel = -1; - QString abiName; - QString package; - Utils::FileName installedLocation; -}; -using SystemImageList = QList; - - -class SdkPlatform -{ -public: - bool isValid() const { return !name.isEmpty() && apiLevel != -1; } - bool operator <(const SdkPlatform &other) const; - int apiLevel = -1; + bool isValid() const { return sdkPlatform && sdkPlatform->isValid() && !name.isEmpty(); } + const SdkPlatform *sdkPlatform = nullptr; QString name; - QString package; - Utils::FileName installedLocation; - SystemImageList systemImages; + QString abi; + int sdcardSize = 0; + QString error; // only used in the return value of createAVD }; -using SdkPlatformList = QList; class ANDROID_EXPORT AndroidConfig { @@ -106,9 +93,8 @@ public: void load(const QSettings &settings); void save(QSettings &settings) const; - static QStringList apiLevelNamesFor(const QList &platforms); - static QString apiLevelNameFor(const SdkPlatform &platform); - QList sdkTargets(int minApiLevel = 0) const; + static QStringList apiLevelNamesFor(const SdkPlatformList &platforms); + static QString apiLevelNameFor(const SdkPlatform *platform); Utils::FileName sdkLocation() const; void setSdkLocation(const Utils::FileName &sdkLocation); @@ -147,19 +133,6 @@ public: Utils::FileName keytoolPath() const; - class CreateAvdInfo - { - public: - bool isValid() const { return target.isValid() && !name.isEmpty(); } - SdkPlatform target; - QString name; - QString abi; - int sdcardSize = 0; - QString error; // only used in the return value of createAVD - }; - - CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, int minApiLevel = 0, QString targetArch = QString()) const; - QVector connectedDevices(QString *error = 0) const; static QVector connectedDevices(const QString &adbToolPath, QString *error = 0); @@ -175,7 +148,6 @@ public: OpenGl getOpenGLEnabled(const QString &emulator) const; bool isConnected(const QString &serialNumber) const; - SdkPlatform highestAndroidSdk() const; private: static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property); @@ -189,7 +161,6 @@ private: bool isBootToQt(const QString &device) const; static QString getAvdName(const QString &serialnumber); - void updateAvailableSdkPlatforms() const; void updateNdkInformation() const; Utils::FileName m_sdkLocation; @@ -201,9 +172,6 @@ private: bool m_automaticKitCreation = true; //caches - mutable bool m_availableSdkPlatformsUpToDate = false; - mutable SdkPlatformList m_availableSdkPlatforms; - mutable bool m_NdkInformationUpToDate = false; mutable QString m_toolchainHost; mutable QVector m_availableNdkPlatforms; @@ -218,6 +186,7 @@ class ANDROID_EXPORT AndroidConfigurations : public QObject public: static const AndroidConfig ¤tConfig(); + static Internal::AndroidSdkManager *sdkManager(); static void setConfig(const AndroidConfig &config); static AndroidConfigurations *instance(); @@ -236,16 +205,17 @@ signals: private: AndroidConfigurations(QObject *parent); + ~AndroidConfigurations(); void load(); void save(); static AndroidConfigurations *m_instance; AndroidConfig m_config; + std::unique_ptr m_sdkManager; QMap > m_defaultDeviceForAbi; bool m_force32bit; }; } // namespace Android -Q_DECLARE_METATYPE(Android::SdkPlatform) diff --git a/src/plugins/android/androiddevicedialog.cpp b/src/plugins/android/androiddevicedialog.cpp index 67cd02e9f84..9c07238d2dc 100644 --- a/src/plugins/android/androiddevicedialog.cpp +++ b/src/plugins/android/androiddevicedialog.cpp @@ -26,6 +26,7 @@ #include "androiddevicedialog.h" #include "androidmanager.h" #include "androidavdmanager.h" +#include "avddialog.h" #include "ui_androiddevicedialog.h" #include @@ -580,9 +581,10 @@ void AndroidDeviceDialog::devicesRefreshed() void AndroidDeviceDialog::createAvd() { m_ui->createAVDButton->setEnabled(false); - AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi); + CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, AndroidConfigurations::sdkManager(), + m_apiLevel, m_abi); - if (!info.target.isValid()) { + if (!info.isValid()) { m_ui->createAVDButton->setEnabled(true); return; } @@ -593,7 +595,7 @@ void AndroidDeviceDialog::createAvd() void AndroidDeviceDialog::avdAdded() { m_ui->createAVDButton->setEnabled(true); - AndroidConfig::CreateAvdInfo info = m_futureWatcherAddDevice.result(); + CreateAvdInfo info = m_futureWatcherAddDevice.result(); if (!info.error.isEmpty()) { QMessageBox::critical(this, QApplication::translate("AndroidConfig", "Error Creating AVD"), info.error); return; diff --git a/src/plugins/android/androiddevicedialog.h b/src/plugins/android/androiddevicedialog.h index f882b0bfcd2..db379c068e9 100644 --- a/src/plugins/android/androiddevicedialog.h +++ b/src/plugins/android/androiddevicedialog.h @@ -79,7 +79,7 @@ private: QString m_defaultDevice; std::unique_ptr m_avdManager; QVector m_connectedDevices; - QFutureWatcher m_futureWatcherAddDevice; + QFutureWatcher m_futureWatcherAddDevice; QFutureWatcher m_futureWatcherRefreshDevices; }; diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 3691d959004..e6fe53bdc37 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -35,6 +35,7 @@ #include "androidqtversion.h" #include "androidbuildapkstep.h" #include "androidavdmanager.h" +#include "androidsdkmanager.h" #include #include @@ -174,7 +175,8 @@ QString AndroidManager::buildTargetSDK(ProjectExplorer::Target *target) if (androidBuildApkStep) return androidBuildApkStep->buildTargetSdk(); - QString fallback = AndroidConfig::apiLevelNameFor(AndroidConfigurations::currentConfig().highestAndroidSdk()); + QString fallback = AndroidConfig::apiLevelNameFor( + AndroidConfigurations::sdkManager()->latestAndroidSdkPlatform()); return fallback; } diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index ac518bdd21c..7bb7a79215e 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -48,13 +48,26 @@ namespace Internal { const QVersionNumber sdkManagerIntroVersion(25, 3 ,0); const char installLocationKey[] = "Installed Location:"; -const char apiLevelPropertyKey[] = "AndroidVersion.ApiLevel"; -const char abiPropertyKey[] = "SystemImage.Abi"; +const char revisionKey[] = "Version:"; +const char descriptionKey[] = "Description:"; const int sdkManagerCmdTimeoutS = 60; using namespace Utils; +int platformNameToApiLevel(const QString &platformName) +{ + int apiLevel = -1; + QRegularExpression re("(android-)(?[0-9]{1,})", + QRegularExpression::CaseInsensitiveOption); + QRegularExpressionMatch match = re.match(platformName); + if (match.hasMatch()) { + QString apiLevelStr = match.captured("apiLevel"); + apiLevel = apiLevelStr.toInt(); + } + return apiLevel; +} + /*! Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns \c true if \a key is found, false otherwise. Result is copied into \a value. @@ -75,7 +88,7 @@ static bool valueForKey(QString key, const QString &line, QString *value = nullp \c true if the command is successfully executed. Output is copied into \a output. The function blocks the calling thread. */ -static bool sdkManagerCommand(const AndroidConfig config, const QStringList &args, QString *output, +static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &args, QString *output, int timeout = sdkManagerCmdTimeoutS) { QString sdkManagerToolPath = config.sdkManagerToolPath().toString(); @@ -88,6 +101,29 @@ static bool sdkManagerCommand(const AndroidConfig config, const QStringList &arg return response.result == SynchronousProcessResponse::Finished; } + +class AndroidSdkManagerPrivate +{ +public: + AndroidSdkManagerPrivate(AndroidSdkManager &sdkManager, const AndroidConfig &config); + ~AndroidSdkManagerPrivate(); + + AndroidSdkPackageList filteredPackages(AndroidSdkPackage::PackageState state, + AndroidSdkPackage::PackageType type, + bool forceUpdate = false); + const AndroidSdkPackageList &allPackages(bool forceUpdate = false); + void refreshSdkPackages(bool forceReload = false); + +private: + void reloadSdkPackages(); + void clearPackages(); + + AndroidSdkManager &m_sdkManager; + const AndroidConfig &m_config; + AndroidSdkPackageList m_allPackages; + FileName lastSdkManagerPath; +}; + /*! \class SdkManagerOutputParser \brief The SdkManagerOutputParser class is a helper class to parse the output of the \c sdkmanager @@ -108,19 +144,20 @@ public: SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker }; + SdkManagerOutputParser(AndroidSdkPackageList &container) : m_packages(container) {} void parsePackageListing(const QString &output); - SdkPlatformList m_installedPlatforms; + AndroidSdkPackageList &m_packages; private: - void compileData(); + void compilePackageAssociations(); void parsePackageData(MarkerTag packageMarker, const QStringList &data); - bool parsePlatform(const QStringList &data, SdkPlatform *platform) const; - bool parseSystemImage(const QStringList &data, SystemImage *image); + AndroidSdkPackage *parsePlatform(const QStringList &data) const; + QPair parseSystemImage(const QStringList &data) const; MarkerTag parseMarkers(const QString &line); MarkerTag m_currentSection = MarkerTag::None; - SystemImageList m_installedSystemImages; + QHash m_systemImages; }; const std::map markerTags { @@ -131,9 +168,9 @@ const std::map markerTags { {SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"} }; -AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config): - m_config(config), - m_parser(new SdkManagerOutputParser) +AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config, QObject *parent): + QObject(parent), + m_d(new AndroidSdkManagerPrivate(*this, config)) { } @@ -142,21 +179,61 @@ AndroidSdkManager::~AndroidSdkManager() } -SdkPlatformList AndroidSdkManager::availableSdkPlatforms(bool *ok) +SdkPlatformList AndroidSdkManager::installedSdkPlatforms() { - bool success = false; - if (m_config.sdkToolsVersion() < sdkManagerIntroVersion) { - AndroidToolManager toolManager(m_config); - return toolManager.availableSdkPlatforms(ok); + AndroidSdkPackageList list = m_d->filteredPackages(AndroidSdkPackage::Installed, + AndroidSdkPackage::SdkPlatformPackage); + return Utils::transform(list, [](AndroidSdkPackage *p) { + return static_cast(p); + }); +} + +const AndroidSdkPackageList &AndroidSdkManager::allSdkPackages() +{ + return m_d->allPackages(); +} + +AndroidSdkPackageList AndroidSdkManager::availableSdkPackages() +{ + return m_d->filteredPackages(AndroidSdkPackage::Available, AndroidSdkPackage::AnyValidType); +} + +AndroidSdkPackageList AndroidSdkManager::installedSdkPackages() +{ + return m_d->filteredPackages(AndroidSdkPackage::Installed, AndroidSdkPackage::AnyValidType); +} + +SdkPlatform *AndroidSdkManager::latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state) +{ + SdkPlatform *result = nullptr; + const AndroidSdkPackageList list = m_d->filteredPackages(state, + AndroidSdkPackage::SdkPlatformPackage); + for (AndroidSdkPackage *p : list) { + auto platform = static_cast(p); + if (!result || result->apiLevel() < platform->apiLevel()) + result = platform; } + return result; +} - QString packageListing; - if (sdkManagerCommand(m_config, QStringList({"--list", "--verbose"}), &packageListing)) - m_parser->parsePackageListing(packageListing); +SdkPlatformList AndroidSdkManager::filteredSdkPlatforms(int minApiLevel, + AndroidSdkPackage::PackageState state) +{ + const AndroidSdkPackageList list = m_d->filteredPackages(state, + AndroidSdkPackage::SdkPlatformPackage); - if (ok) - *ok = success; - return m_parser->m_installedPlatforms; + SdkPlatformList result; + for (AndroidSdkPackage *p : list) { + auto platform = static_cast(p); + if (platform && platform->apiLevel() >= minApiLevel) + result << platform; + } + return result; +} + +void AndroidSdkManager::reloadPackages(bool forceReload) +{ + m_d->refreshSdkPackages(forceReload); } void SdkManagerOutputParser::parsePackageListing(const QString &output) @@ -173,10 +250,9 @@ void SdkManagerOutputParser::parsePackageListing(const QString &output) } }; - QRegularExpression delimiters("[\n\r]"); + QRegularExpression delimiters("[\\n\\r]"); foreach (QString outputLine, output.split(delimiters)) { MarkerTag marker = parseMarkers(outputLine.trimmed()); - if (marker & SectionMarkers) { // Section marker found. Update the current section being parsed. m_currentSection = marker; @@ -211,20 +287,27 @@ void SdkManagerOutputParser::parsePackageListing(const QString &output) packageData << outputLine; } } - compileData(); - Utils::sort(m_installedPlatforms); + compilePackageAssociations(); } -void SdkManagerOutputParser::compileData() +void SdkManagerOutputParser::compilePackageAssociations() { // Associate the system images with sdk platforms. - for (auto &image : m_installedSystemImages) { - auto findPlatfom = [image](const SdkPlatform &platform) { - return platform.apiLevel == image.apiLevel; + auto imageItr = m_systemImages.cbegin(); + while (imageItr != m_systemImages.cend()) { + auto findPlatform = [imageItr](const AndroidSdkPackage *p) { + const SdkPlatform *platform = nullptr; + if (p->type() == AndroidSdkPackage::SdkPlatformPackage) + platform = static_cast(p); + return platform && platform->apiLevel() == imageItr.value(); }; - auto itr = std::find_if(m_installedPlatforms.begin(), m_installedPlatforms.end(), findPlatfom); - if (itr != m_installedPlatforms.end()) - itr->systemImages.append(image); + auto itr = std::find_if(m_packages.begin(), m_packages.end(), + findPlatform); + if (itr != m_packages.end()) { + SdkPlatform *platform = static_cast(*itr); + platform->addSystemImage(static_cast(imageItr.key())); + } + ++imageItr; } } @@ -235,24 +318,23 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt if (m_currentSection != MarkerTag::InstalledPackagesMarker) return; // For now, only interested in installed packages. + AndroidSdkPackage *package = nullptr; switch (packageMarker) { case MarkerTag::PlatformMarker: - { - SdkPlatform platform; - if (parsePlatform(data, &platform)) - m_installedPlatforms.append(platform); - else - qCDebug(sdkManagerLog) << "Platform: Parsing failed: " << data; - } + package = parsePlatform(data); + if (package) + m_packages.append(package); break; case MarkerTag::SystemImageMarker: { - SystemImage image; - if (parseSystemImage(data, &image)) - m_installedSystemImages.append(image); - else + QPair result = parseSystemImage(data); + if (result.first) { + m_systemImages[result.first] = result.second; + package = result.first; + } else { qCDebug(sdkManagerLog) << "System Image: Parsing failed: " << data; + } } break; @@ -260,71 +342,118 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt qCDebug(sdkManagerLog) << "Unhandled package: " << markerTags.at(packageMarker); break; } -} -bool SdkManagerOutputParser::parsePlatform(const QStringList &data, SdkPlatform *platform) const -{ - QTC_ASSERT(platform && !data.isEmpty(), return false); - - QStringList parts = data.at(0).split(';'); - if (parts.count() < 2) { - qCDebug(sdkManagerLog) << "Platform: Unexpected header: "<< data; - return false; - } - platform->name = parts[1]; - platform->package = data.at(0); - - foreach (QString line, data) { - QString value; - if (valueForKey(installLocationKey, line, &value)) - platform->installedLocation = Utils::FileName::fromString(value); - } - - int apiLevel = AndroidManager::findApiLevel(platform->installedLocation); - if (apiLevel != -1) - platform->apiLevel = apiLevel; - else - qCDebug(sdkManagerLog) << "Platform: Can not parse api level: "<< data; - - return apiLevel != -1; -} - -bool SdkManagerOutputParser::parseSystemImage(const QStringList &data, SystemImage *image) -{ - QTC_ASSERT(image && !data.isEmpty(), return false); - - QStringList parts = data.at(0).split(';'); - QTC_ASSERT(!data.isEmpty() && parts.count() >= 4, - qCDebug(sdkManagerLog) << "System Image: Unexpected header: " << data); - - image->package = data.at(0); - foreach (QString line, data) { - QString value; - if (valueForKey(installLocationKey, line, &value)) - image->installedLocation = Utils::FileName::fromString(value); - } - - Utils::FileName propertiesPath = image->installedLocation; - propertiesPath.appendPath("/source.properties"); - if (propertiesPath.exists()) { - // Installed System Image. - QSettings imageProperties(propertiesPath.toString(), QSettings::IniFormat); - bool validApiLevel = false; - image->apiLevel = imageProperties.value(apiLevelPropertyKey).toInt(&validApiLevel); - if (!validApiLevel) { - qCDebug(sdkManagerLog) << "System Image: Can not parse api level: "<< data; - return false; + if (package) { + switch (m_currentSection) { + case MarkerTag::InstalledPackagesMarker: + package->setState(AndroidSdkPackage::Installed); + break; + case MarkerTag::AvailablePackagesMarkers: + case MarkerTag::AvailableUpdatesMarker: + package->setState(AndroidSdkPackage::Available); + break; + default: + qCDebug(sdkManagerLog) << "Invalid section marker: " << markerTags.at(m_currentSection); + break; } - image->abiName = imageProperties.value(abiPropertyKey).toString(); - } else if (parts.count() >= 4){ - image->apiLevel = parts[1].section('-', 1, 1).toInt(); - image->abiName = parts[3]; + } +} + +AndroidSdkPackage *SdkManagerOutputParser::parsePlatform(const QStringList &data) const +{ + QTC_ASSERT(!data.isEmpty(), qCDebug(sdkManagerLog) << "Platform: Empty input"; return nullptr); + + QStringList parts = data.at(0).split(';'); + QTC_ASSERT(parts.count() >= 2, + qCDebug(sdkManagerLog) << "Platform: Unexpected header:"<< data; return nullptr); + + int apiLevel = platformNameToApiLevel(parts.at(1)); + if (apiLevel == -1) { + qCDebug(sdkManagerLog) << "Platform: Can not parse api level:"<< data; + return nullptr; + } + + QVersionNumber revision; + QString description; + Utils::FileName installedLocation; + foreach (QString line, data) { + QString value; + if (valueForKey(installLocationKey, line, &value)) { + installedLocation = Utils::FileName::fromString(value); + continue; + } + + if (valueForKey(revisionKey, line, &value)) { + revision = QVersionNumber::fromString(value); + continue; + } + + if (valueForKey(descriptionKey, line, &value)) { + description = value; + continue; + } + } + + SdkPlatform *platform = nullptr; + if (!revision.isNull() && apiLevel != -1) { + platform = new SdkPlatform(revision, data.at(0), apiLevel); + platform->setDescriptionText(description); + platform->setInstalledLocation(installedLocation); + } else { + qCDebug(sdkManagerLog) << "Platform: Parsing failed. Minimum required data unavailable:" + << data; + } + + return platform; +} + +QPair SdkManagerOutputParser::parseSystemImage(const QStringList &data) const +{ + QPair result(nullptr, -1); + QTC_ASSERT(!data.isEmpty(), + qCDebug(sdkManagerLog) << "System Image: Empty input"; return result); + + QStringList parts = data.at(0).split(';'); + QTC_ASSERT(parts.count() >= 4, + qCDebug(sdkManagerLog) << "System Image: Unexpected header:" << data; return result); + + int apiLevel = platformNameToApiLevel(parts.at(1)); + if (apiLevel == -1) { + qCDebug(sdkManagerLog) << "System Image: Can not parse api level:"<< data; + return result; + } + + QVersionNumber revision; + QString description; + Utils::FileName installedLocation; + foreach (QString line, data) { + QString value; + if (valueForKey(installLocationKey, line, &value)) { + installedLocation = Utils::FileName::fromString(value); + continue; + } + + if (valueForKey(revisionKey, line, &value)) { + revision = QVersionNumber::fromString(value); + continue; + } + + if (valueForKey(descriptionKey, line, &value)) { + description = value; + continue; + } + } + + if (!revision.isNull()) { + auto image = new SystemImage(revision, data.at(0), parts.at(3)); + image->setInstalledLocation(installedLocation); + image->setDisplayText(description); + image->setDescriptionText(description); + result = qMakePair(image, apiLevel); } else { qCDebug(sdkManagerLog) << "System Image: Can not parse: "<< data; - return false; } - - return true; + return result; } SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QString &line) @@ -340,5 +469,79 @@ SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QSt return None; } +AndroidSdkManagerPrivate::AndroidSdkManagerPrivate(AndroidSdkManager &sdkManager, + const AndroidConfig &config): + m_sdkManager(sdkManager), + m_config(config) +{ + +} + +AndroidSdkManagerPrivate::~AndroidSdkManagerPrivate() +{ + clearPackages(); +} + +AndroidSdkPackageList +AndroidSdkManagerPrivate::filteredPackages(AndroidSdkPackage::PackageState state, + AndroidSdkPackage::PackageType type, bool forceUpdate) +{ + refreshSdkPackages(forceUpdate); + return Utils::filtered(m_allPackages, [state, type](const AndroidSdkPackage *p) { + return p->state() & state && p->type() & type; + }); +} + +const AndroidSdkPackageList &AndroidSdkManagerPrivate::allPackages(bool forceUpdate) +{ + refreshSdkPackages(forceUpdate); + return m_allPackages; +} + +void AndroidSdkManagerPrivate::reloadSdkPackages() +{ + m_sdkManager.packageReloadBegin(); + clearPackages(); + + lastSdkManagerPath = m_config.sdkManagerToolPath(); + + if (m_config.sdkToolsVersion().isNull()) { + // Configuration has invalid sdk path or corrupt installation. + m_sdkManager.packageReloadFinished(); + return; + } + + if (m_config.sdkToolsVersion() < sdkManagerIntroVersion) { + // Old Sdk tools. + AndroidToolManager toolManager(m_config); + auto toAndroidSdkPackages = [](SdkPlatform *p) -> AndroidSdkPackage *{ + return p; + }; + m_allPackages = Utils::transform(toolManager.availableSdkPlatforms(), toAndroidSdkPackages); + } else { + QString packageListing; + if (sdkManagerCommand(m_config, QStringList({"--list", "--verbose"}), &packageListing)) { + SdkManagerOutputParser parser(m_allPackages); + parser.parsePackageListing(packageListing); + } + } + m_sdkManager.packageReloadFinished(); +} + +void AndroidSdkManagerPrivate::refreshSdkPackages(bool forceReload) +{ + // Sdk path changed. Updated packages. + // QTC updates the package listing only + if (m_config.sdkManagerToolPath() != lastSdkManagerPath || forceReload) + reloadSdkPackages(); +} + +void AndroidSdkManagerPrivate::clearPackages() +{ + for (AndroidSdkPackage *p : m_allPackages) + delete p; + m_allPackages.clear(); +} + } // namespace Internal } // namespace Android diff --git a/src/plugins/android/androidsdkmanager.h b/src/plugins/android/androidsdkmanager.h index b1da078b6ef..3f2880c66fe 100644 --- a/src/plugins/android/androidsdkmanager.h +++ b/src/plugins/android/androidsdkmanager.h @@ -25,26 +25,46 @@ #pragma once #include "utils/fileutils.h" -#include "androidconfigurations.h" +#include "androidsdkpackage.h" + +#include #include namespace Android { + +class AndroidConfig; + namespace Internal { -class SdkManagerOutputParser; +class AndroidSdkManagerPrivate; -class AndroidSdkManager +class AndroidSdkManager : public QObject { + Q_OBJECT public: - AndroidSdkManager(const AndroidConfig &config); + AndroidSdkManager(const AndroidConfig &config, QObject *parent = nullptr); ~AndroidSdkManager(); - SdkPlatformList availableSdkPlatforms(bool *ok = nullptr); + SdkPlatformList installedSdkPlatforms(); + const AndroidSdkPackageList &allSdkPackages(); + AndroidSdkPackageList availableSdkPackages(); + AndroidSdkPackageList installedSdkPackages(); + + SdkPlatform *latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state + = AndroidSdkPackage::Installed); + SdkPlatformList filteredSdkPlatforms(int minApiLevel, + AndroidSdkPackage::PackageState state + = AndroidSdkPackage::Installed); + void reloadPackages(bool forceReload = false); + +signals: + void packageReloadBegin(); + void packageReloadFinished(); private: - const AndroidConfig &m_config; - std::unique_ptr m_parser; + std::unique_ptr m_d; + friend class AndroidSdkManagerPrivate; }; } // namespace Internal diff --git a/src/plugins/android/androidsdkpackage.cpp b/src/plugins/android/androidsdkpackage.cpp new file mode 100644 index 00000000000..0ea964af97d --- /dev/null +++ b/src/plugins/android/androidsdkpackage.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ +#include "androidsdkpackage.h" + +namespace Android { + +AndroidSdkPackage::AndroidSdkPackage(QVersionNumber version, QString sdkStylePathStr, + QObject *parent) : + QObject(parent), + m_revision(version), + m_sdkStylePath(sdkStylePathStr) +{ + +} + +bool AndroidSdkPackage::operator <(const AndroidSdkPackage &other) const +{ + if (typeid(*this) != typeid(other)) + return type() < other.type(); + return displayText() < other.displayText(); +} + +QString AndroidSdkPackage::displayText() const +{ + return m_displayText; +} + +QString AndroidSdkPackage::descriptionText() const +{ + return m_descriptionText; +} + +const QVersionNumber &AndroidSdkPackage::revision() const +{ + return m_revision; +} + +AndroidSdkPackage::PackageState AndroidSdkPackage::state() const +{ + return m_state; +} + +const QString &AndroidSdkPackage::sdkStylePath() const +{ + return m_sdkStylePath; +} + +const Utils::FileName &AndroidSdkPackage::installedLocation() const +{ + return m_installedLocation; +} + +void AndroidSdkPackage::setDisplayText(const QString &str) +{ + m_displayText = str; +} + +void AndroidSdkPackage::setDescriptionText(const QString &str) +{ + m_descriptionText = str; +} + +void AndroidSdkPackage::setState(AndroidSdkPackage::PackageState state) +{ + m_state = state; +} + +void AndroidSdkPackage::setInstalledLocation(const Utils::FileName &path) +{ + m_installedLocation = path; + if (m_installedLocation.exists()) + updatePackageDetails(); +} + +void AndroidSdkPackage::updatePackageDetails() +{ + +} + +SystemImage::SystemImage(QVersionNumber version, QString sdkStylePathStr, QString abi, + SdkPlatform *platform): + AndroidSdkPackage(version, sdkStylePathStr, platform), + m_platform(platform), + m_abiName(abi) +{ +} + +bool SystemImage::isValid() const +{ + return m_platform && m_platform->isValid(); +} + +AndroidSdkPackage::PackageType SystemImage::type() const +{ + return SystemImagePackage; +} + +const QString &SystemImage::abiName() const +{ + return m_abiName; +} + +const SdkPlatform *SystemImage::platform() const +{ + return m_platform.data(); +} + +void SystemImage::setPlatform(SdkPlatform *platform) +{ + m_platform = platform; +} + +SdkPlatform::SdkPlatform(QVersionNumber version, QString sdkStylePathStr, int api, QObject *parent) : + AndroidSdkPackage(version, sdkStylePathStr, parent), + m_apiLevel(api) +{ + setDisplayText(QString("android-%1") + .arg(m_apiLevel != -1 ? QString::number(m_apiLevel) : "Unknown")); +} + +SdkPlatform::~SdkPlatform() +{ + for (SystemImage *image : m_systemImages) + delete image; + m_systemImages.clear(); +} + +bool SdkPlatform::isValid() const +{ + return m_apiLevel != -1; +} + +AndroidSdkPackage::PackageType SdkPlatform::type() const +{ + return SdkPlatformPackage; +} + +bool SdkPlatform::operator <(const AndroidSdkPackage &other) const +{ + if (typeid(*this) != typeid(other)) + return AndroidSdkPackage::operator <(other); + + const SdkPlatform &platform = static_cast(other); + if (platform.m_apiLevel == m_apiLevel) + return AndroidSdkPackage::operator <(other); + + return platform.m_apiLevel < m_apiLevel; +} + +int SdkPlatform::apiLevel() const +{ + return m_apiLevel; +} + +QVersionNumber SdkPlatform::version() const +{ + return m_version; +} + +void SdkPlatform::addSystemImage(SystemImage *image) +{ + m_systemImages.append(image); + image->setPlatform(this); +} + +const SystemImageList &SdkPlatform::systemImages() const +{ + return m_systemImages; +} + +} // namespace Android diff --git a/src/plugins/android/androidsdkpackage.h b/src/plugins/android/androidsdkpackage.h new file mode 100644 index 00000000000..2ffbd3b4a87 --- /dev/null +++ b/src/plugins/android/androidsdkpackage.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ +#include "utils/fileutils.h" + +#include +#include +#include +#include + +#pragma once + +namespace Android { + +namespace Internal { + class SdkManagerOutputParser; + class AndroidToolOutputParser; +} +class SdkPlatform; +class SystemImage; + +class AndroidSdkPackage : public QObject +{ + Q_OBJECT + +public: + enum PackageType { + UnknownPackage = 1 << 0, + SdkToolsPackage = 1 << 1, + BuildToolsPackage = 1 << 2, + PlatformToolsPackage = 1 << 3, + SdkPlatformPackage = 1 << 4, + SystemImagePackage = 1 << 5, + AnyValidType = SdkToolsPackage | BuildToolsPackage | PlatformToolsPackage | + SdkPlatformPackage | SystemImagePackage + }; + + enum PackageState { + Unknown = 1 << 0, + Installed = 1 << 1, + Available = 1 << 2, + AnyValidState = Installed | Available + }; + + AndroidSdkPackage(QVersionNumber revision, QString sdkStylePathStr, QObject *parent = nullptr); + virtual ~AndroidSdkPackage() { } + + virtual bool isValid() const = 0; + virtual PackageType type() const = 0; + virtual bool operator <(const AndroidSdkPackage &other) const; + + QString displayText() const; + QString descriptionText() const; + const QVersionNumber &revision() const; + PackageState state() const; + const QString &sdkStylePath() const; + const Utils::FileName &installedLocation() const; + +protected: + void setDisplayText(const QString &str); + void setDescriptionText(const QString &str); + void setState(PackageState state); + void setInstalledLocation(const Utils::FileName &path); + + virtual void updatePackageDetails(); + +private: + QString m_displayText; + QString m_descriptionText; + QVersionNumber m_revision; + PackageState m_state = PackageState::Unknown; + QString m_sdkStylePath; + Utils::FileName m_installedLocation; + + friend class Internal::SdkManagerOutputParser; + friend class Internal::AndroidToolOutputParser; +}; +using AndroidSdkPackageList = QList; + +class SystemImage : public AndroidSdkPackage +{ + Q_OBJECT +public: + SystemImage(QVersionNumber revision, QString sdkStylePathStr, QString abi, + SdkPlatform *platform = nullptr); + +// AndroidSdkPackage Overrides + bool isValid() const override; + PackageType type() const override; + + const QString &abiName() const; + const SdkPlatform *platform() const; + void setPlatform(SdkPlatform *platform); + +private: + QPointer m_platform; + QString m_abiName; +}; +using SystemImageList = QList; + + +class SdkPlatform : public AndroidSdkPackage +{ + Q_OBJECT +public: + SdkPlatform(QVersionNumber revision, QString sdkStylePathStr, int api, + QObject *parent = nullptr); + + ~SdkPlatform(); + +// AndroidSdkPackage Overrides + bool isValid() const override; + PackageType type() const override; + bool operator <(const AndroidSdkPackage &other) const override; + + int apiLevel() const; + QVersionNumber version() const; + void addSystemImage(SystemImage *image); + const SystemImageList &systemImages() const; + +private: + SystemImageList m_systemImages; + int m_apiLevel = -1; + QVersionNumber m_version; +}; +using SdkPlatformList = QList; +} // namespace Android + + diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 72c896038f6..4a7149847e7 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -31,6 +31,8 @@ #include "androidconstants.h" #include "androidtoolchain.h" #include "androidavdmanager.h" +#include "androidsdkmanager.h" +#include "avddialog.h" #include #include @@ -212,7 +214,8 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent) : QWidget(parent), m_ui(new Ui_AndroidSettingsWidget), m_androidConfig(AndroidConfigurations::currentConfig()), - m_avdManager(new AndroidAvdManager(m_androidConfig)) + m_avdManager(new AndroidAvdManager(m_androidConfig)), + m_sdkManager(new AndroidSdkManager(m_androidConfig)) { m_ui->setupUi(this); @@ -390,7 +393,7 @@ void AndroidSettingsWidget::validateSdk() summaryWidget->setPointValid(BuildToolsInstalledRow, !m_androidConfig.buildToolsVersion().isNull()); summaryWidget->setPointValid(PlatformSdkInstalledRow, - !m_androidConfig.sdkTargets().isEmpty()); + !m_sdkManager->installedSdkPlatforms().isEmpty()); updateUI(); } @@ -412,9 +415,9 @@ void AndroidSettingsWidget::openOpenJDKDownloadUrl() void AndroidSettingsWidget::addAVD() { disableAvdControls(); - AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this); + CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, m_sdkManager.get()); - if (!info.target.isValid()) { + if (!info.isValid()) { enableAvdControls(); return; } @@ -424,7 +427,7 @@ void AndroidSettingsWidget::addAVD() void AndroidSettingsWidget::avdAdded() { - AndroidConfig::CreateAvdInfo info = m_futureWatcher.result(); + CreateAvdInfo info = m_futureWatcher.result(); if (!info.error.isEmpty()) { enableAvdControls(); QMessageBox::critical(this, QApplication::translate("AndroidConfig", "Error Creating AVD"), info.error); diff --git a/src/plugins/android/androidsettingswidget.h b/src/plugins/android/androidsettingswidget.h index 41c6a32650b..5d81ad8333f 100644 --- a/src/plugins/android/androidsettingswidget.h +++ b/src/plugins/android/androidsettingswidget.h @@ -100,11 +100,12 @@ private: Ui_AndroidSettingsWidget *m_ui; AndroidConfig m_androidConfig; AvdModel m_AVDModel; - QFutureWatcher m_futureWatcher; + QFutureWatcher m_futureWatcher; QFutureWatcher m_virtualDevicesWatcher; QString m_lastAddedAvd; std::unique_ptr m_avdManager; + std::unique_ptr m_sdkManager; }; } // namespace Internal diff --git a/src/plugins/android/androidtoolmanager.cpp b/src/plugins/android/androidtoolmanager.cpp index 39fa2a2c426..fc214ab1139 100644 --- a/src/plugins/android/androidtoolmanager.cpp +++ b/src/plugins/android/androidtoolmanager.cpp @@ -47,7 +47,7 @@ class AndroidToolOutputParser { public: void parseTargetListing(const QString &output, const FileName &sdkLocation, - SdkPlatformList *platformList); + SdkPlatformList &platformList); QList m_installedPlatforms; }; @@ -104,7 +104,7 @@ SdkPlatformList AndroidToolManager::availableSdkPlatforms(bool *ok) const QString targetListing; if (androidToolCommand(m_config.androidToolPath(), QStringList({"list", "target"}), androidToolEnvironment(), &targetListing)) { - m_parser->parseTargetListing(targetListing, m_config.sdkLocation(), &list); + m_parser->parseTargetListing(targetListing, m_config.sdkLocation(), list); success = true; } else { qCDebug(androidToolLog) << "Android tool target listing failed"; @@ -121,8 +121,7 @@ void AndroidToolManager::launchAvdManager() const QProcess::startDetached(m_config.androidToolPath().toString(), QStringList("avd")); } -QFuture -AndroidToolManager::createAvd(AndroidConfig::CreateAvdInfo info) const +QFuture AndroidToolManager::createAvd(CreateAvdInfo info) const { return Utils::runAsync(&AndroidToolManager::createAvdImpl, info, m_config.androidToolPath(), androidToolEnvironment()); @@ -159,15 +158,14 @@ Environment AndroidToolManager::androidToolEnvironment() const return env; } -AndroidConfig::CreateAvdInfo AndroidToolManager::createAvdImpl(AndroidConfig::CreateAvdInfo info, - FileName androidToolPath, - Environment env) +CreateAvdInfo AndroidToolManager::createAvdImpl(CreateAvdInfo info, FileName androidToolPath, + Environment env) { QProcess proc; proc.setProcessEnvironment(env.toProcessEnvironment()); QStringList arguments; arguments << QLatin1String("create") << QLatin1String("avd") - << QLatin1String("-t") << AndroidConfig::apiLevelNameFor(info.target) + << QLatin1String("-t") << AndroidConfig::apiLevelNameFor(info.sdkPlatform) << QLatin1String("-n") << info.name << QLatin1String("-b") << info.abi; if (info.sdcardSize > 0) @@ -293,24 +291,36 @@ AndroidDeviceInfoList AndroidToolManager::androidVirtualDevices(const Utils::Fil void AndroidToolOutputParser::parseTargetListing(const QString &output, const Utils::FileName &sdkLocation, - SdkPlatformList *platformList) + SdkPlatformList &platformList) { - if (!platformList) - return; - - auto addSystemImage = [](const QStringList& abiList, SdkPlatform &platform) { + auto addSystemImage = [](const QStringList& abiList, SdkPlatform *platform) { + QTC_ASSERT(platform, return); foreach (auto imageAbi, abiList) { - SystemImage image; - image.abiName = imageAbi; - image.apiLevel = platform.apiLevel; - platform.systemImages.append(image); + auto image = new SystemImage(QVersionNumber(), "", imageAbi, platform); + platform->addSystemImage(image); } }; - SdkPlatform platform; - QStringList abiList; - foreach (const QString &l, output.split('\n')) { - const QString line = l.trimmed(); + class { + public: + QStringList abiList; + QVersionNumber revision; + int apiLevel = -1; + QString description; + Utils::FileName installedLocation; + + void clear() { + abiList.clear(); + revision = QVersionNumber(); + apiLevel = -1; + description.clear(); + installedLocation.clear(); + } + } platformParams; + + QStringList outputLines = output.split('\n'); + for (int index = 0; index < outputLines.count(); ++index) { + const QString line = outputLines.at(index).trimmed(); if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) { int index = line.indexOf(QLatin1String("\"android-")); if (index == -1) @@ -319,33 +329,33 @@ void AndroidToolOutputParser::parseTargetListing(const QString &output, const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1); Utils::FileName platformPath = sdkLocation; platformPath.appendPath(QString("/platforms/android-%1").arg(tmp)); - platform.installedLocation = platformPath; - platform.apiLevel = AndroidManager::findApiLevel(platformPath); + platformParams.installedLocation = platformPath; + platformParams.apiLevel = AndroidManager::findApiLevel(platformPath); } else if (line.startsWith(QLatin1String("Name:"))) { - platform.name = line.mid(6); + platformParams.description = line.mid(6); + } else if (line.startsWith(QLatin1String("Revision:"))) { + platformParams.revision = QVersionNumber::fromString(line.mid(10)); } else if (line.startsWith(QLatin1String("Tag/ABIs :"))) { - abiList = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", "))); + platformParams.abiList = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", "))); } else if (line.startsWith(QLatin1String("ABIs"))) { - abiList = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", "))); - } else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) { - if (platform.apiLevel == -1) + platformParams.abiList = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", "))); + } else if (line.startsWith(QLatin1String("---")) + || line.startsWith(QLatin1String("===")) + || index == outputLines.count() - 1) { + if (platformParams.apiLevel == -1) continue; - - addSystemImage(abiList, platform); - *platformList << platform; - - platform = SdkPlatform(); - abiList.clear(); + auto platform = new SdkPlatform(platformParams.revision, + QString("platforms;android-%1").arg(platformParams.apiLevel), + platformParams.apiLevel); + platform->setState(AndroidSdkPackage::Installed); + platform->setDescriptionText(platformParams.description); + platform->setInstalledLocation(platformParams.installedLocation); + addSystemImage(platformParams.abiList, platform); + platformList << platform; + platformParams.clear(); } } - - // The last parsed Platform. - if (platform.apiLevel != -1) { - addSystemImage(abiList, platform); - *platformList << platform; - } - - Utils::sort(*platformList); + Utils::sort(platformList); } } // namespace Internal diff --git a/src/plugins/android/androidtoolmanager.h b/src/plugins/android/androidtoolmanager.h index faaa9428ab6..61f7018bf92 100644 --- a/src/plugins/android/androidtoolmanager.h +++ b/src/plugins/android/androidtoolmanager.h @@ -51,15 +51,15 @@ public: SdkPlatformList availableSdkPlatforms(bool *ok = nullptr) const; void launchAvdManager() const; - QFuture createAvd(AndroidConfig::CreateAvdInfo info) const; + QFuture createAvd(CreateAvdInfo info) const; bool removeAvd(const QString &name) const; QFuture androidVirtualDevicesFuture() const; // Helper methods private: Utils::Environment androidToolEnvironment() const; - static AndroidConfig::CreateAvdInfo createAvdImpl(AndroidConfig::CreateAvdInfo info, - Utils::FileName androidToolPath, Utils::Environment env); + static CreateAvdInfo createAvdImpl(CreateAvdInfo info, Utils::FileName androidToolPath, + Utils::Environment env); static AndroidDeviceInfoList androidVirtualDevices(const Utils::FileName &androidTool, const Utils::FileName &sdkLlocationPath, const Utils::Environment &environment); diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index 03d29d4e232..702b2120d79 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -24,11 +24,12 @@ ****************************************************************************/ #include "avddialog.h" -#include "androidconfigurations.h" +#include "androidsdkmanager.h" #include #include #include +#include #include #include @@ -37,10 +38,14 @@ using namespace Android; using namespace Android::Internal; -AvdDialog::AvdDialog(int minApiLevel, const QString &targetArch, const AndroidConfig *config, QWidget *parent) : - QDialog(parent), m_config(config), m_minApiLevel(minApiLevel), +AvdDialog::AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QString &targetArch, + QWidget *parent) : + QDialog(parent), + m_sdkManager(sdkManager), + m_minApiLevel(minApiLevel), m_allowedNameChars(QLatin1String("[a-z|A-Z|0-9|._-]*")) { + QTC_CHECK(m_sdkManager); m_avdDialog.setupUi(this); m_hideTipTimer.setInterval(2000); m_hideTipTimer.setSingleShot(true); @@ -70,12 +75,27 @@ AvdDialog::AvdDialog(int minApiLevel, const QString &targetArch, const AndroidCo bool AvdDialog::isValid() const { - return !name().isEmpty() && target().isValid() && !abi().isEmpty(); + return !name().isEmpty() && sdkPlatform() && sdkPlatform()->isValid() && !abi().isEmpty(); } -SdkPlatform AvdDialog::target() const +CreateAvdInfo AvdDialog::gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager, + int minApiLevel, QString targetArch) { - return m_avdDialog.targetComboBox->currentData().value(); + CreateAvdInfo result; + AvdDialog d(minApiLevel, sdkManager, targetArch, parent); + if (d.exec() != QDialog::Accepted || !d.isValid()) + return result; + + result.sdkPlatform = d.sdkPlatform(); + result.name = d.name(); + result.abi = d.abi(); + result.sdcardSize = d.sdcardSize(); + return result; +} + +const SdkPlatform* AvdDialog::sdkPlatform() const +{ + return m_avdDialog.targetComboBox->currentData().value(); } QString AvdDialog::name() const @@ -96,21 +116,23 @@ int AvdDialog::sdcardSize() const void AvdDialog::updateApiLevelComboBox() { SdkPlatformList filteredList; - SdkPlatformList platforms = m_config->sdkTargets(m_minApiLevel); + const SdkPlatformList platforms = m_sdkManager->filteredSdkPlatforms(m_minApiLevel); QString selectedAbi = abi(); - auto hasAbi = [selectedAbi](const SystemImage &image) { - return image.isValid() && (image.abiName == selectedAbi); + auto hasAbi = [selectedAbi](const SystemImage *image) { + return image && image->isValid() && (image->abiName() == selectedAbi); }; - filteredList = Utils::filtered(platforms, [hasAbi](const SdkPlatform &platform) { - return Utils::anyOf(platform.systemImages,hasAbi); + filteredList = Utils::filtered(platforms, [hasAbi](const SdkPlatform *platform) { + return platform && Utils::anyOf(platform->systemImages(), hasAbi); }); m_avdDialog.targetComboBox->clear(); - foreach (const SdkPlatform &platform, filteredList) { - m_avdDialog.targetComboBox->addItem(AndroidConfig::apiLevelNameFor(platform), - QVariant::fromValue(platform)); + for (SdkPlatform *platform: filteredList) { + m_avdDialog.targetComboBox->addItem(platform->displayText(), + QVariant::fromValue(platform)); + m_avdDialog.targetComboBox->setItemData(m_avdDialog.targetComboBox->count() - 1, + platform->descriptionText(), Qt::ToolTipRole); } if (platforms.isEmpty()) { diff --git a/src/plugins/android/avddialog.h b/src/plugins/android/avddialog.h index d22e6af64f5..8a30b613c6b 100644 --- a/src/plugins/android/avddialog.h +++ b/src/plugins/android/avddialog.h @@ -24,7 +24,7 @@ ****************************************************************************/ #pragma once - +#include "androidconfigurations.h" #include "ui_addnewavddialog.h" #include @@ -35,26 +35,28 @@ class AndroidConfig; class SdkPlatform; namespace Internal { - +class AndroidSdkManager; class AvdDialog : public QDialog { Q_OBJECT public: - explicit AvdDialog(int minApiLevel, const QString &targetArch, - const AndroidConfig *config, QWidget *parent = 0); + explicit AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QString &targetArch, + QWidget *parent = 0); - Android::SdkPlatform target() const; + const SdkPlatform *sdkPlatform() const; QString name() const; QString abi() const; int sdcardSize() const; bool isValid() const; + static CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager, + int minApiLevel = 0, QString targetArch = QString()); private: void updateApiLevelComboBox(); bool eventFilter(QObject *obj, QEvent *event); Ui::AddNewAVDDialog m_avdDialog; - const AndroidConfig *m_config; + AndroidSdkManager *m_sdkManager; int m_minApiLevel; QTimer m_hideTipTimer; QRegExp m_allowedNameChars; From 4c4500c605d692db50d42af9842c11c6cf38b959 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 31 Aug 2017 13:30:10 +0200 Subject: [PATCH 16/27] Android: Make SDK package parsing logic generic Task-number: QTCREATORBUG-18978 Change-Id: I7e643842d6378a172fcfe5984cec931621ff9c41 Reviewed-by: BogDan Vatra --- src/plugins/android/androidsdkmanager.cpp | 161 +++++++++++----------- 1 file changed, 79 insertions(+), 82 deletions(-) diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index 7bb7a79215e..c538454da93 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -131,6 +131,17 @@ private: */ class SdkManagerOutputParser { + class GenericPackageData + { + public: + bool isValid() const { return !revision.isNull() && !description.isNull(); } + QStringList headerParts; + QVersionNumber revision; + QString description; + Utils::FileName installedLocation; + QMap extraData; + }; + public: enum MarkerTag { @@ -152,6 +163,8 @@ public: private: void compilePackageAssociations(); void parsePackageData(MarkerTag packageMarker, const QStringList &data); + bool parseAbstractData(GenericPackageData &output, const QStringList &input, int minParts, + const QString &logStrTag, QStringList extraKeys = QStringList()) const; AndroidSdkPackage *parsePlatform(const QStringList &data) const; QPair parseSystemImage(const QStringList &data) const; MarkerTag parseMarkers(const QString &line); @@ -319,11 +332,15 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt return; // For now, only interested in installed packages. AndroidSdkPackage *package = nullptr; + auto createPackage = [&](std::function creator) { + if ((package = creator(this, data))) + m_packages.append(package); + }; + switch (packageMarker) { case MarkerTag::PlatformMarker: - package = parsePlatform(data); - if (package) - m_packages.append(package); + createPackage(&SdkManagerOutputParser::parsePlatform); break; case MarkerTag::SystemImageMarker: @@ -332,8 +349,6 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt if (result.first) { m_systemImages[result.first] = result.second; package = result.first; - } else { - qCDebug(sdkManagerLog) << "System Image: Parsing failed: " << data; } } break; @@ -359,99 +374,81 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt } } +bool SdkManagerOutputParser::parseAbstractData(SdkManagerOutputParser::GenericPackageData &output, + const QStringList &input, int minParts, + const QString &logStrTag, + QStringList extraKeys) const +{ + if (input.isEmpty()) { + qCDebug(sdkManagerLog) << logStrTag + ": Empty input"; + return false; + } + + output.headerParts = input.at(0).split(';'); + if (output.headerParts.count() < minParts) { + qCDebug(sdkManagerLog) << logStrTag + "%1: Unexpected header:" << input; + return false; + } + + extraKeys << installLocationKey << revisionKey << descriptionKey; + foreach (QString line, input) { + QString value; + for (auto key: extraKeys) { + if (valueForKey(key, line, &value)) { + if (key == installLocationKey) + output.installedLocation = Utils::FileName::fromString(value); + else if (key == revisionKey) + output.revision = QVersionNumber::fromString(value); + else if (key == descriptionKey) + output.description = value; + else + output.extraData[key] = value; + break; + } + } + } + + return output.isValid(); +} + AndroidSdkPackage *SdkManagerOutputParser::parsePlatform(const QStringList &data) const { - QTC_ASSERT(!data.isEmpty(), qCDebug(sdkManagerLog) << "Platform: Empty input"; return nullptr); - - QStringList parts = data.at(0).split(';'); - QTC_ASSERT(parts.count() >= 2, - qCDebug(sdkManagerLog) << "Platform: Unexpected header:"<< data; return nullptr); - - int apiLevel = platformNameToApiLevel(parts.at(1)); - if (apiLevel == -1) { - qCDebug(sdkManagerLog) << "Platform: Can not parse api level:"<< data; - return nullptr; - } - - QVersionNumber revision; - QString description; - Utils::FileName installedLocation; - foreach (QString line, data) { - QString value; - if (valueForKey(installLocationKey, line, &value)) { - installedLocation = Utils::FileName::fromString(value); - continue; - } - - if (valueForKey(revisionKey, line, &value)) { - revision = QVersionNumber::fromString(value); - continue; - } - - if (valueForKey(descriptionKey, line, &value)) { - description = value; - continue; - } - } - SdkPlatform *platform = nullptr; - if (!revision.isNull() && apiLevel != -1) { - platform = new SdkPlatform(revision, data.at(0), apiLevel); - platform->setDescriptionText(description); - platform->setInstalledLocation(installedLocation); + GenericPackageData packageData; + if (parseAbstractData(packageData, data, 2, "Platform")) { + int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1)); + if (apiLevel == -1) { + qCDebug(sdkManagerLog) << "Platform: Can not parse api level:"<< data; + return nullptr; + } + platform = new SdkPlatform(packageData.revision, data.at(0), apiLevel); + platform->setDescriptionText(packageData.description); + platform->setInstalledLocation(packageData.installedLocation); } else { qCDebug(sdkManagerLog) << "Platform: Parsing failed. Minimum required data unavailable:" << data; } - return platform; } QPair SdkManagerOutputParser::parseSystemImage(const QStringList &data) const { QPair result(nullptr, -1); - QTC_ASSERT(!data.isEmpty(), - qCDebug(sdkManagerLog) << "System Image: Empty input"; return result); - - QStringList parts = data.at(0).split(';'); - QTC_ASSERT(parts.count() >= 4, - qCDebug(sdkManagerLog) << "System Image: Unexpected header:" << data; return result); - - int apiLevel = platformNameToApiLevel(parts.at(1)); - if (apiLevel == -1) { - qCDebug(sdkManagerLog) << "System Image: Can not parse api level:"<< data; - return result; - } - - QVersionNumber revision; - QString description; - Utils::FileName installedLocation; - foreach (QString line, data) { - QString value; - if (valueForKey(installLocationKey, line, &value)) { - installedLocation = Utils::FileName::fromString(value); - continue; + GenericPackageData packageData; + if (parseAbstractData(packageData, data, 4, "System-image")) { + int apiLevel = platformNameToApiLevel(packageData.headerParts.at(1)); + if (apiLevel == -1) { + qCDebug(sdkManagerLog) << "System-image: Can not parse api level:"<< data; + return result; } - - if (valueForKey(revisionKey, line, &value)) { - revision = QVersionNumber::fromString(value); - continue; - } - - if (valueForKey(descriptionKey, line, &value)) { - description = value; - continue; - } - } - - if (!revision.isNull()) { - auto image = new SystemImage(revision, data.at(0), parts.at(3)); - image->setInstalledLocation(installedLocation); - image->setDisplayText(description); - image->setDescriptionText(description); + auto image = new SystemImage(packageData.revision, data.at(0), + packageData.headerParts.at(3)); + image->setInstalledLocation(packageData.installedLocation); + image->setDisplayText(packageData.description); + image->setDescriptionText(packageData.description); result = qMakePair(image, apiLevel); } else { - qCDebug(sdkManagerLog) << "System Image: Can not parse: "<< data; + qCDebug(sdkManagerLog) << "System-image: Minimum required data unavailable: "<< data; } return result; } From 0ca97ab2bdb6c9f7d9ae10a6614e371f8e270cca Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Wed, 30 Aug 2017 13:35:07 +0200 Subject: [PATCH 17/27] Android: Enable parsing of available Android SDK packages Task-number: QTCREATORBUG-18978 Change-Id: Icb9ba0a3afc3ca4386f056aa9e3de057dc9cdf7b Reviewed-by: BogDan Vatra --- src/plugins/android/androidsdkmanager.cpp | 55 +++++++++++++++-------- src/plugins/android/androidsdkpackage.cpp | 8 +++- src/plugins/android/androidsdkpackage.h | 3 +- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index c538454da93..4b28b47fade 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -273,12 +273,8 @@ void SdkManagerOutputParser::parsePackageListing(const QString &output) continue; } - if (m_currentSection == None - || m_currentSection == AvailablePackagesMarkers // At this point. Not interested in - || m_currentSection == AvailableUpdatesMarker) { // available or update packages. - // Let go of verbose output utill a valid section starts. - continue; - } + if (m_currentSection == None) + continue; // Continue with the verbose output until a valid section starts. if (marker == EmptyMarker) { // Empty marker. Occurs at the end of a package details. @@ -305,22 +301,48 @@ void SdkManagerOutputParser::parsePackageListing(const QString &output) void SdkManagerOutputParser::compilePackageAssociations() { + // Return true if package p is already installed i.e. there exists a installed package having + // same sdk style path and same revision as of p. + auto isInstalled = [](const AndroidSdkPackageList &container, AndroidSdkPackage *p) { + return Utils::anyOf(container, [p](AndroidSdkPackage *other) { + return other->state() == AndroidSdkPackage::Installed && + other->sdkStylePath() == p->sdkStylePath() && + other->revision() == p->revision(); + }); + }; + + auto deleteAlreadyInstalled = [isInstalled](AndroidSdkPackageList &packages) { + for (auto p = packages.begin(); p != packages.end();) { + if ((*p)->state() == AndroidSdkPackage::Available && isInstalled(packages, *p)) { + delete *p; + p = packages.erase(p); + } else { + ++p; + } + } + }; + + // Remove already installed packages. + deleteAlreadyInstalled(m_packages); + + // Filter out available images that are already installed. + AndroidSdkPackageList images = m_systemImages.keys(); + deleteAlreadyInstalled(images); + // Associate the system images with sdk platforms. - auto imageItr = m_systemImages.cbegin(); - while (imageItr != m_systemImages.cend()) { - auto findPlatform = [imageItr](const AndroidSdkPackage *p) { + for (AndroidSdkPackage *image : images) { + int imageApi = m_systemImages[image]; + auto itr = std::find_if(m_packages.begin(), m_packages.end(), + [imageApi](const AndroidSdkPackage *p) { const SdkPlatform *platform = nullptr; if (p->type() == AndroidSdkPackage::SdkPlatformPackage) platform = static_cast(p); - return platform && platform->apiLevel() == imageItr.value(); - }; - auto itr = std::find_if(m_packages.begin(), m_packages.end(), - findPlatform); + return platform && platform->apiLevel() == imageApi; + }); if (itr != m_packages.end()) { SdkPlatform *platform = static_cast(*itr); - platform->addSystemImage(static_cast(imageItr.key())); + platform->addSystemImage(static_cast(image)); } - ++imageItr; } } @@ -328,9 +350,6 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt { QTC_ASSERT(!data.isEmpty() && packageMarker != None, return); - if (m_currentSection != MarkerTag::InstalledPackagesMarker) - return; // For now, only interested in installed packages. - AndroidSdkPackage *package = nullptr; auto createPackage = [&](std::function creator) { diff --git a/src/plugins/android/androidsdkpackage.cpp b/src/plugins/android/androidsdkpackage.cpp index 0ea964af97d..93fbc53dca7 100644 --- a/src/plugins/android/androidsdkpackage.cpp +++ b/src/plugins/android/androidsdkpackage.cpp @@ -24,6 +24,8 @@ ****************************************************************************/ #include "androidsdkpackage.h" +#include "utils/algorithm.h" + namespace Android { AndroidSdkPackage::AndroidSdkPackage(QVersionNumber version, QString sdkStylePathStr, @@ -185,9 +187,11 @@ void SdkPlatform::addSystemImage(SystemImage *image) image->setPlatform(this); } -const SystemImageList &SdkPlatform::systemImages() const +SystemImageList SdkPlatform::systemImages(PackageState state) const { - return m_systemImages; + return Utils::filtered(m_systemImages, [state](const SystemImage *image) { + return image->state() & state; + }); } } // namespace Android diff --git a/src/plugins/android/androidsdkpackage.h b/src/plugins/android/androidsdkpackage.h index 2ffbd3b4a87..3a96f1b373d 100644 --- a/src/plugins/android/androidsdkpackage.h +++ b/src/plugins/android/androidsdkpackage.h @@ -137,7 +137,8 @@ public: int apiLevel() const; QVersionNumber version() const; void addSystemImage(SystemImage *image); - const SystemImageList &systemImages() const; + SystemImageList systemImages(AndroidSdkPackage::PackageState state + = AndroidSdkPackage::Installed) const; private: SystemImageList m_systemImages; From daee6f6f33a23b1f423853f3bf6bd558125318b1 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 31 Aug 2017 10:34:46 +0200 Subject: [PATCH 18/27] Android: Enable Android build, platform and SDK tools parsing Task-number: QTCREATORBUG-18978 Change-Id: I84d4ead3180ab818493aff9975a7a9b813fba8d0 Reviewed-by: BogDan Vatra --- src/plugins/android/androidsdkmanager.cpp | 93 ++++++++++++++++++++--- src/plugins/android/androidsdkpackage.cpp | 47 ++++++++++++ src/plugins/android/androidsdkpackage.h | 33 ++++++++ 3 files changed, 161 insertions(+), 12 deletions(-) diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index 4b28b47fade..446460f8224 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -145,13 +145,16 @@ class SdkManagerOutputParser public: enum MarkerTag { - None = 0x01, - InstalledPackagesMarker = 0x02, - AvailablePackagesMarkers = 0x04, - AvailableUpdatesMarker = 0x08, - EmptyMarker = 0x10, - PlatformMarker = 0x20, - SystemImageMarker = 0x40, + None = 0x001, + InstalledPackagesMarker = 0x002, + AvailablePackagesMarkers = 0x004, + AvailableUpdatesMarker = 0x008, + EmptyMarker = 0x010, + PlatformMarker = 0x020, + SystemImageMarker = 0x040, + BuildToolsMarker = 0x080, + SdkToolsMarker = 0x100, + PlatformToolsMarker = 0x200, SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker }; @@ -167,6 +170,9 @@ private: const QString &logStrTag, QStringList extraKeys = QStringList()) const; AndroidSdkPackage *parsePlatform(const QStringList &data) const; QPair parseSystemImage(const QStringList &data) const; + BuildTools *parseBuildToolsPackage(const QStringList &data) const; + SdkTools *parseSdkToolsPackage(const QStringList &data) const; + PlatformTools *parsePlatformToolsPackage(const QStringList &data) const; MarkerTag parseMarkers(const QString &line); MarkerTag m_currentSection = MarkerTag::None; @@ -174,11 +180,14 @@ private: }; const std::map markerTags { - {SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"}, - {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"}, - {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Updates:"}, - {SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"}, - {SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"} + {SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"}, + {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"}, + {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Updates:"}, + {SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"}, + {SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"}, + {SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"}, + {SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"}, + {SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"} }; AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config, QObject *parent): @@ -358,6 +367,18 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt }; switch (packageMarker) { + case MarkerTag::BuildToolsMarker: + createPackage(&SdkManagerOutputParser::parseBuildToolsPackage); + break; + + case MarkerTag::SdkToolsMarker: + createPackage(&SdkManagerOutputParser::parseSdkToolsPackage); + break; + + case MarkerTag::PlatformToolsMarker: + createPackage(&SdkManagerOutputParser::parsePlatformToolsPackage); + break; + case MarkerTag::PlatformMarker: createPackage(&SdkManagerOutputParser::parsePlatform); break; @@ -472,6 +493,54 @@ QPair SdkManagerOutputParser::parseSystemImage(const QString return result; } +BuildTools *SdkManagerOutputParser::parseBuildToolsPackage(const QStringList &data) const +{ + BuildTools *buildTools = nullptr; + GenericPackageData packageData; + if (parseAbstractData(packageData, data, 2, "Build-tools")) { + buildTools = new BuildTools(packageData.revision, data.at(0)); + buildTools->setDescriptionText(packageData.description); + buildTools->setDisplayText(packageData.description); + buildTools->setInstalledLocation(packageData.installedLocation); + } else { + qCDebug(sdkManagerLog) << "Build-tools: Parsing failed. Minimum required data unavailable:" + << data; + } + return buildTools; +} + +SdkTools *SdkManagerOutputParser::parseSdkToolsPackage(const QStringList &data) const +{ + SdkTools *sdkTools = nullptr; + GenericPackageData packageData; + if (parseAbstractData(packageData, data, 1, "SDK-tools")) { + sdkTools = new SdkTools(packageData.revision, data.at(0)); + sdkTools->setDescriptionText(packageData.description); + sdkTools->setDisplayText(packageData.description); + sdkTools->setInstalledLocation(packageData.installedLocation); + } else { + qCDebug(sdkManagerLog) << "SDK-tools: Parsing failed. Minimum required data unavailable:" + << data; + } + return sdkTools; +} + +PlatformTools *SdkManagerOutputParser::parsePlatformToolsPackage(const QStringList &data) const +{ + PlatformTools *platformTools = nullptr; + GenericPackageData packageData; + if (parseAbstractData(packageData, data, 1, "Platform-tools")) { + platformTools = new PlatformTools(packageData.revision, data.at(0)); + platformTools->setDescriptionText(packageData.description); + platformTools->setDisplayText(packageData.description); + platformTools->setInstalledLocation(packageData.installedLocation); + } else { + qCDebug(sdkManagerLog) << "Platform-tools: Parsing failed. Minimum required data " + "unavailable:" << data; + } + return platformTools; +} + SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QString &line) { if (line.isEmpty()) diff --git a/src/plugins/android/androidsdkpackage.cpp b/src/plugins/android/androidsdkpackage.cpp index 93fbc53dca7..44174cee906 100644 --- a/src/plugins/android/androidsdkpackage.cpp +++ b/src/plugins/android/androidsdkpackage.cpp @@ -194,4 +194,51 @@ SystemImageList SdkPlatform::systemImages(PackageState state) const }); } +BuildTools::BuildTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent): + AndroidSdkPackage(revision, sdkStylePathStr, parent) +{ +} + +bool BuildTools::isValid() const +{ + return true; +} + +AndroidSdkPackage::PackageType BuildTools::type() const +{ + return AndroidSdkPackage::BuildToolsPackage; +} + +SdkTools::SdkTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent): + AndroidSdkPackage(revision, sdkStylePathStr, parent) +{ + +} + +bool SdkTools::isValid() const +{ + return true; +} + +AndroidSdkPackage::PackageType SdkTools::type() const +{ + return AndroidSdkPackage::SdkToolsPackage; +} + +PlatformTools::PlatformTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent): + AndroidSdkPackage(revision, sdkStylePathStr, parent) +{ + +} + +bool PlatformTools::isValid() const +{ + return true; +} + +AndroidSdkPackage::PackageType PlatformTools::type() const +{ + return AndroidSdkPackage::PlatformToolsPackage; +} + } // namespace Android diff --git a/src/plugins/android/androidsdkpackage.h b/src/plugins/android/androidsdkpackage.h index 3a96f1b373d..486c604b604 100644 --- a/src/plugins/android/androidsdkpackage.h +++ b/src/plugins/android/androidsdkpackage.h @@ -146,6 +146,39 @@ private: QVersionNumber m_version; }; using SdkPlatformList = QList; + +class BuildTools : public AndroidSdkPackage +{ +public: + BuildTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent = nullptr); + +// AndroidSdkPackage Overrides +public: + bool isValid() const override; + PackageType type() const override; +}; + +class PlatformTools : public AndroidSdkPackage +{ +public: + PlatformTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent = nullptr); + +// AndroidSdkPackage Overrides +public: + bool isValid() const override; + PackageType type() const override; +}; + +class SdkTools : public AndroidSdkPackage +{ +public: + SdkTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent = nullptr); + +// AndroidSdkPackage Overrides +public: + bool isValid() const override; + PackageType type() const override; +}; } // namespace Android From e70179e14f5baf680259658b1c022a5a4926db4f Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Fri, 14 Jul 2017 19:35:48 +0200 Subject: [PATCH 19/27] Android: Add API to check whether to use native SDK UI tools Task-number: QTCREATORBUG-18978 Change-Id: I1cf4749e81b3b49de14ff88a5876569886d2cdfe Reviewed-by: BogDan Vatra --- src/plugins/android/androidavdmanager.cpp | 15 ++++----------- src/plugins/android/androidavdmanager.h | 1 - src/plugins/android/androidconfigurations.cpp | 6 ++++++ src/plugins/android/androidconfigurations.h | 2 ++ src/plugins/android/androidsettingswidget.cpp | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index f4cf488616f..5af95e7a260 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -54,8 +54,6 @@ const char avdInfoAbiKey[] = "abi.type"; const char avdInfoTargetKey[] = "target"; const char avdInfoErrorKey[] = "Error:"; -const QVersionNumber avdManagerIntroVersion(25, 3 ,0); - const int avdCreateTimeoutMs = 30000; /*! @@ -219,14 +217,9 @@ AndroidAvdManager::~AndroidAvdManager() } -bool AndroidAvdManager::avdManagerUiToolAvailable() const -{ - return m_config.sdkToolsVersion() < avdManagerIntroVersion; -} - void AndroidAvdManager::launchAvdManagerUiTool() const { - if (avdManagerUiToolAvailable()) { + if (m_config.useNativeUiTools()) { m_androidTool->launchAvdManager(); } else { qCDebug(avdManagerLog) << "AVD Ui tool launch failed. UI tool not available" @@ -236,7 +229,7 @@ void AndroidAvdManager::launchAvdManagerUiTool() const QFuture AndroidAvdManager::createAvd(CreateAvdInfo info) const { - if (m_config.sdkToolsVersion() < avdManagerIntroVersion) + if (m_config.useNativeUiTools()) return m_androidTool->createAvd(info); return Utils::runAsync(&createAvdCommand, m_config, info); @@ -244,7 +237,7 @@ QFuture AndroidAvdManager::createAvd(CreateAvdInfo info) const bool AndroidAvdManager::removeAvd(const QString &name) const { - if (m_config.sdkToolsVersion() < avdManagerIntroVersion) + if (m_config.useNativeUiTools()) return m_androidTool->removeAvd(name); Utils::SynchronousProcess proc; @@ -257,7 +250,7 @@ bool AndroidAvdManager::removeAvd(const QString &name) const QFuture AndroidAvdManager::avdList() const { - if (m_config.sdkToolsVersion() < avdManagerIntroVersion) + if (m_config.useNativeUiTools()) return m_androidTool->androidVirtualDevicesFuture(); return Utils::runAsync(&AvdManagerOutputParser::listVirtualDevices, m_parser.get(), m_config); diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h index 5e079444e3d..8bb487ca663 100644 --- a/src/plugins/android/androidavdmanager.h +++ b/src/plugins/android/androidavdmanager.h @@ -40,7 +40,6 @@ public: AndroidAvdManager(const AndroidConfig& config = AndroidConfigurations::currentConfig()); ~AndroidAvdManager(); - bool avdManagerUiToolAvailable() const; void launchAvdManagerUiTool() const; QFuture createAvd(CreateAvdInfo info) const; bool removeAvd(const QString &name) const; diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 12a2e9e991d..ca9c39a743a 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -676,6 +676,12 @@ QStringList AndroidConfig::getAbis(const QString &adbToolPath, const QString &de return result; } +bool AndroidConfig::useNativeUiTools() const +{ + const QVersionNumber version = sdkToolsVersion(); + return !version.isNull() && version <= QVersionNumber(25, 3 ,0); +} + QString AndroidConfig::bestNdkPlatformMatch(int target) const { target = std::max(9, target); diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 9fc349d267c..0adcc8d2d36 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -148,6 +148,8 @@ public: OpenGl getOpenGLEnabled(const QString &emulator) const; bool isConnected(const QString &serialNumber) const; + bool useNativeUiTools() const; + private: static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property); diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 4a7149847e7..3842b15e692 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -535,7 +535,7 @@ void AndroidSettingsWidget::updateUI() void AndroidSettingsWidget::manageAVD() { - if (m_avdManager->avdManagerUiToolAvailable()) { + if (m_androidConfig.useNativeUiTools()) { m_avdManager->launchAvdManagerUiTool(); } else { QMessageBox::warning(this, tr("AVD Manager Not Available"), From 4b1429de55af44af46ca80f7104fdd249aaa98d8 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Tue, 12 Sep 2017 09:34:02 +0200 Subject: [PATCH 20/27] Android: Add APIs to install, uninstall and update SDK packages Task-number: QTCREATORBUG-18978 Change-Id: I3e19d665fb5b8a3a562010484735022d7ed333a1 Reviewed-by: BogDan Vatra --- src/plugins/android/androidsdkmanager.cpp | 206 ++++++++++++++++++++-- src/plugins/android/androidsdkmanager.h | 23 +++ 2 files changed, 218 insertions(+), 11 deletions(-) diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index 446460f8224..b8dcfc9f620 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -29,9 +29,11 @@ #include "utils/algorithm.h" #include "utils/qtcassert.h" +#include "utils/runextensions.h" #include "utils/synchronousprocess.h" #include "utils/environment.h" +#include #include #include #include @@ -52,8 +54,10 @@ const char revisionKey[] = "Version:"; const char descriptionKey[] = "Description:"; const int sdkManagerCmdTimeoutS = 60; +const int sdkManagerOperationTimeoutS = 600; using namespace Utils; +using SdkCmdFutureInterface = QFutureInterface; int platformNameToApiLevel(const QString &platformName) { @@ -83,24 +87,84 @@ static bool valueForKey(QString key, const QString &line, QString *value = nullp return false; } -/*! - Runs the \c sdkmanger tool specific to configuration \a config with arguments \a args. Returns - \c true if the command is successfully executed. Output is copied into \a output. The function - blocks the calling thread. - */ -static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &args, QString *output, - int timeout = sdkManagerCmdTimeoutS) +int parseProgress(const QString &out) +{ + int progress = -1; + if (out.isEmpty()) + return progress; + QRegularExpression reg("(?\\d*)%"); + QStringList lines = out.split(QRegularExpression("[\\n\\r]"), QString::SkipEmptyParts); + for (const QString &line : lines) { + QRegularExpressionMatch match = reg.match(line); + if (match.hasMatch()) { + progress = match.captured("progress").toInt(); + if (progress < 0 || progress > 100) + progress = -1; + } + } + return progress; +} + +void watcherDeleter(QFutureWatcher *watcher) +{ + if (!watcher->isFinished() && !watcher->isCanceled()) + watcher->cancel(); + + if (!watcher->isFinished()) + watcher->waitForFinished(); + + delete watcher; +} + +/*! + Runs the \c sdkmanger tool with arguments \a args. Returns \c true if the command is + successfully executed. Output is copied into \a output. The function blocks the calling thread. + */ +static bool sdkManagerCommand(const Utils::FileName &toolPath, const QStringList &args, + QString *output, int timeout = sdkManagerCmdTimeoutS) { - QString sdkManagerToolPath = config.sdkManagerToolPath().toString(); SynchronousProcess proc; proc.setTimeoutS(timeout); proc.setTimeOutMessageBoxEnabled(true); - SynchronousProcessResponse response = proc.run(sdkManagerToolPath, args); + SynchronousProcessResponse response = proc.run(toolPath.toString(), args); if (output) *output = response.allOutput(); return response.result == SynchronousProcessResponse::Finished; } +/*! + Runs the \c sdkmanger tool with arguments \a args. The operation command progress is updated in + to the future interface \a fi and \a output is populated with command output. The command listens + to cancel signal emmitted by \a sdkManager and kill the commands. The command is also killed + after the lapse of \a timeout seconds. The function blocks the calling thread. + */ +static void sdkManagerCommand(const Utils::FileName &toolPath, const QStringList &args, + AndroidSdkManager &sdkManager, SdkCmdFutureInterface &fi, + AndroidSdkManager::OperationOutput &output, double progressQuota, + bool interruptible = true, int timeout = sdkManagerOperationTimeoutS) +{ + int offset = fi.progressValue(); + SynchronousProcess proc; + proc.setStdErrBufferedSignalsEnabled(true); + proc.setStdOutBufferedSignalsEnabled(true); + proc.setTimeoutS(timeout); + QObject::connect(&proc, &SynchronousProcess::stdOutBuffered, + [offset, progressQuota, &fi](const QString &out) { + int progressPercent = parseProgress(out); + if (progressPercent != -1) + fi.setProgressValue(offset + qRound((progressPercent / 100.0) * progressQuota)); + }); + QObject::connect(&proc, &SynchronousProcess::stdErrBuffered, [&output](const QString &err) { + output.stdError = err; + }); + if (interruptible) { + QObject::connect(&sdkManager, &AndroidSdkManager::cancelActiveOperations, + &proc, &SynchronousProcess::terminate); + } + SynchronousProcessResponse response = proc.run(toolPath.toString(), args); + output.success = response.result == SynchronousProcessResponse::Finished; +} + class AndroidSdkManagerPrivate { @@ -114,6 +178,14 @@ public: const AndroidSdkPackageList &allPackages(bool forceUpdate = false); void refreshSdkPackages(bool forceReload = false); + void updateInstalled(SdkCmdFutureInterface &fi); + void update(SdkCmdFutureInterface &fi, const QStringList &install, + const QStringList &uninstall); + + void addWatcher(const QFuture &future); + + std::unique_ptr, decltype(&watcherDeleter)> m_activeOperation; + private: void reloadSdkPackages(); void clearPackages(); @@ -198,7 +270,7 @@ AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config, QObject *paren AndroidSdkManager::~AndroidSdkManager() { - + cancelOperatons(); } SdkPlatformList AndroidSdkManager::installedSdkPlatforms() @@ -258,6 +330,37 @@ void AndroidSdkManager::reloadPackages(bool forceReload) m_d->refreshSdkPackages(forceReload); } +bool AndroidSdkManager::isBusy() const +{ + return m_d->m_activeOperation && !m_d->m_activeOperation->isFinished(); +} + +QFuture AndroidSdkManager::updateAll() +{ + if (isBusy()) { + return QFuture(); + } + auto future = Utils::runAsync(&AndroidSdkManagerPrivate::updateInstalled, m_d.get()); + m_d->addWatcher(future); + return future; +} + +QFuture +AndroidSdkManager::update(const QStringList &install, const QStringList &uninstall) +{ + if (isBusy()) + return QFuture(); + auto future = Utils::runAsync(&AndroidSdkManagerPrivate::update, m_d.get(), install, uninstall); + m_d->addWatcher(future); + return future; +} + +void AndroidSdkManager::cancelOperatons() +{ + emit cancelActiveOperations(); + m_d->m_activeOperation.reset(); +} + void SdkManagerOutputParser::parsePackageListing(const QString &output) { QStringList packageData; @@ -556,6 +659,7 @@ SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QSt AndroidSdkManagerPrivate::AndroidSdkManagerPrivate(AndroidSdkManager &sdkManager, const AndroidConfig &config): + m_activeOperation(nullptr, watcherDeleter), m_sdkManager(sdkManager), m_config(config) { @@ -605,7 +709,8 @@ void AndroidSdkManagerPrivate::reloadSdkPackages() m_allPackages = Utils::transform(toolManager.availableSdkPlatforms(), toAndroidSdkPackages); } else { QString packageListing; - if (sdkManagerCommand(m_config, QStringList({"--list", "--verbose"}), &packageListing)) { + if (sdkManagerCommand(m_config.sdkManagerToolPath(), QStringList({"--list", "--verbose"}), + &packageListing)) { SdkManagerOutputParser parser(m_allPackages); parser.parsePackageListing(packageListing); } @@ -621,6 +726,85 @@ void AndroidSdkManagerPrivate::refreshSdkPackages(bool forceReload) reloadSdkPackages(); } +void AndroidSdkManagerPrivate::updateInstalled(SdkCmdFutureInterface &fi) +{ + fi.setProgressRange(0, 100); + fi.setProgressValue(0); + AndroidSdkManager::OperationOutput result; + result.type = AndroidSdkManager::UpdateAll; + result.stdOutput = QCoreApplication::translate("AndroidSdkManager", + "Updating installed packages."); + fi.reportResult(result); + QStringList args("--update"); + if (!fi.isCanceled()) + sdkManagerCommand(m_config.sdkManagerToolPath(), args, m_sdkManager, fi, result, 100); + else + qCDebug(sdkManagerLog) << "Update: Operation cancelled before start"; + + if (result.stdError.isEmpty() && !result.success) + result.stdError = QCoreApplication::translate("AndroidSdkManager", "Failed."); + result.stdOutput = QCoreApplication::translate("AndroidSdkManager", "Done\n\n"); + fi.reportResult(result); + fi.setProgressValue(100); +} + +void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringList &install, + const QStringList &uninstall) +{ + fi.setProgressRange(0, 100); + fi.setProgressValue(0); + double progressQuota = 100.0 / (install.count() + uninstall.count()); + int currentProgress = 0; + + QString installTag = QCoreApplication::translate("AndroidSdkManager", "Installing"); + QString uninstallTag = QCoreApplication::translate("AndroidSdkManager", "Uninstalling"); + + auto doOperation = [&](const QString& packagePath, const QStringList& args, + bool isInstall) { + AndroidSdkManager::OperationOutput result; + result.type = AndroidSdkManager::UpdatePackage; + result.stdOutput = QString("%1 %2").arg(isInstall ? installTag : uninstallTag) + .arg(packagePath); + fi.reportResult(result); + if (fi.isCanceled()) { + qCDebug(sdkManagerLog) << args << "Update: Operation cancelled before start"; + } else { + sdkManagerCommand(m_config.sdkManagerToolPath(), args, m_sdkManager, fi, result, + progressQuota, isInstall); + } + currentProgress += progressQuota; + fi.setProgressValue(currentProgress); + if (result.stdError.isEmpty() && !result.success) + result.stdError = QCoreApplication::translate("AndroidSdkManager", "Failed"); + result.stdOutput = QCoreApplication::translate("AndroidSdkManager", "Done\n\n"); + fi.reportResult(result); + return fi.isCanceled(); + }; + + + // Uninstall packages + for (const QString &sdkStylePath : uninstall) { + // Uninstall operations are not interptible. We don't want to leave half uninstalled. + if (doOperation(sdkStylePath, {"--uninstall", sdkStylePath}, false)) + break; + } + + // Install packages + for (const QString &sdkStylePath : install) { + if (doOperation(sdkStylePath, {sdkStylePath}, true)) + break; + } + fi.setProgressValue(100); +} + +void AndroidSdkManagerPrivate::addWatcher(const QFuture &future) +{ + if (future.isFinished()) + return; + m_activeOperation.reset(new QFutureWatcher()); + m_activeOperation->setFuture(future); +} + void AndroidSdkManagerPrivate::clearPackages() { for (AndroidSdkPackage *p : m_allPackages) diff --git a/src/plugins/android/androidsdkmanager.h b/src/plugins/android/androidsdkmanager.h index 3f2880c66fe..fc41fb594ed 100644 --- a/src/plugins/android/androidsdkmanager.h +++ b/src/plugins/android/androidsdkmanager.h @@ -28,6 +28,7 @@ #include "androidsdkpackage.h" #include +#include #include @@ -43,6 +44,21 @@ class AndroidSdkManager : public QObject { Q_OBJECT public: + enum CommandType + { + None, + UpdateAll, + UpdatePackage + }; + + struct OperationOutput + { + bool success = false; + CommandType type = None; + QString stdOutput; + QString stdError; + }; + AndroidSdkManager(const AndroidConfig &config, QObject *parent = nullptr); ~AndroidSdkManager(); @@ -58,9 +74,16 @@ public: = AndroidSdkPackage::Installed); void reloadPackages(bool forceReload = false); + bool isBusy() const; + QFuture updateAll(); + QFuture update(const QStringList &install, const QStringList &uninstall); + + void cancelOperatons(); + signals: void packageReloadBegin(); void packageReloadFinished(); + void cancelActiveOperations(); private: std::unique_ptr m_d; From 476b133e91390ff7061901f9e68cf96f2ff99404 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Mon, 18 Sep 2017 13:48:00 +0200 Subject: [PATCH 21/27] Android: Android SDK manager user interface Task-number: QTCREATORBUG-18978 Change-Id: I421ea66fcd4f3cf38e6cfd3be58a35b3f9204c6f Reviewed-by: BogDan Vatra Reviewed-by: Leena Miettinen --- src/plugins/android/android.pro | 11 +- .../android/androidsdkmanagerwidget.cpp | 364 ++++++++++++++++++ src/plugins/android/androidsdkmanagerwidget.h | 87 +++++ .../android/androidsdkmanagerwidget.ui | 272 +++++++++++++ src/plugins/android/androidsdkmodel.cpp | 316 +++++++++++++++ src/plugins/android/androidsdkmodel.h | 83 ++++ src/plugins/android/androidsdkpackage.cpp | 14 +- src/plugins/android/androidsettingswidget.cpp | 78 +++- src/plugins/android/androidsettingswidget.h | 3 + src/plugins/android/androidsettingswidget.ui | 293 ++++++++------ 10 files changed, 1381 insertions(+), 140 deletions(-) create mode 100644 src/plugins/android/androidsdkmanagerwidget.cpp create mode 100644 src/plugins/android/androidsdkmanagerwidget.h create mode 100644 src/plugins/android/androidsdkmanagerwidget.ui create mode 100644 src/plugins/android/androidsdkmodel.cpp create mode 100644 src/plugins/android/androidsdkmodel.h diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index 6cd59c4b705..3c2a5669b5c 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -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 diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp new file mode 100644 index 00000000000..42b0234610f --- /dev/null +++ b/src/plugins/android/androidsdkmanagerwidget.cpp @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ +#include "androidsdkmanagerwidget.h" + +#include "ui_androidsdkmanagerwidget.h" +#include "androidconfigurations.h" +#include "androidsdkmodel.h" + +#include "utils/outputformatter.h" +#include "utils/runextensions.h" +#include "utils/qtcassert.h" +#include "utils/utilsicons.h" + +#include +#include +#include +#include + +namespace { +Q_LOGGING_CATEGORY(androidSdkMgrUiLog, "qtc.android.sdkManagerUi") +} + +namespace Android { +namespace Internal { + +using namespace std::placeholders; + +class PackageFilterModel : public QSortFilterProxyModel +{ +public: + PackageFilterModel(AndroidSdkModel* sdkModel); + + void setAcceptedPackageState(AndroidSdkPackage::PackageState state); + bool filterAcceptsRow(int source_row, const QModelIndex &sourceParent) const override; + +private: + AndroidSdkPackage::PackageState m_packageState = AndroidSdkPackage::AnyValidState; +}; + +AndroidSdkManagerWidget::AndroidSdkManagerWidget(const AndroidConfig &config, + AndroidSdkManager *sdkManager, QWidget *parent) : + QWidget(parent), + m_androidConfig(config), + m_sdkManager(sdkManager), + m_sdkModel(new AndroidSdkModel(m_sdkManager, this)), + m_ui(new Ui::AndroidSdkManagerWidget) +{ + m_ui->setupUi(this); + m_ui->warningLabel->setElideMode(Qt::ElideRight); + m_ui->warningIconLabel->setPixmap(Utils::Icons::WARNING.pixmap()); + m_ui->viewStack->setCurrentWidget(m_ui->packagesStack); + + m_formatter = new Utils::OutputFormatter; + m_formatter->setPlainTextEdit(m_ui->outputEdit); + + connect(m_sdkModel, &AndroidSdkModel::dataChanged, [this]() { + if (m_ui->viewStack->currentWidget() == m_ui->packagesStack) + m_ui->applySelectionButton->setEnabled(!m_sdkModel->userSelection().isEmpty()); + }); + + connect(m_sdkModel, &AndroidSdkModel::modelAboutToBeReset, [this]() { + m_ui->applySelectionButton->setEnabled(false); + m_ui->expandCheck->setChecked(false); + cancelPendingOperations(); + switchView(PackageListing); + }); + + auto proxyModel = new PackageFilterModel(m_sdkModel); + m_ui->packagesView->setModel(proxyModel); + m_ui->packagesView->header()->setSectionResizeMode(AndroidSdkModel::packageNameColumn, + QHeaderView::ResizeToContents); + m_ui->packagesView->header()->setSectionResizeMode(AndroidSdkModel::apiLevelColumn, + QHeaderView::ResizeToContents); + m_ui->packagesView->header()->setSectionResizeMode(AndroidSdkModel::packageRevisionColumn, + QHeaderView::ResizeToContents); + connect(m_ui->expandCheck, &QCheckBox::stateChanged, [this](int state) { + if (state == Qt::Checked) + m_ui->packagesView->expandAll(); + else + m_ui->packagesView->collapseAll(); + }); + connect(m_ui->updateInstalledButton, &QPushButton::clicked, + this, &AndroidSdkManagerWidget::onUpdatePackages); + connect(m_ui->showAllRadio, &QRadioButton::toggled, [this, proxyModel](bool checked) { + if (checked) { + proxyModel->setAcceptedPackageState(AndroidSdkPackage::AnyValidState); + m_sdkModel->resetSelection(); + } + }); + connect(m_ui->showInstalledRadio, &QRadioButton::toggled, [this, proxyModel](bool checked) { + if (checked) { + proxyModel->setAcceptedPackageState(AndroidSdkPackage::Installed); + m_sdkModel->resetSelection(); + } + }); + connect(m_ui->showAvailableRadio, &QRadioButton::toggled, [this, proxyModel](bool checked) { + if (checked) { + proxyModel->setAcceptedPackageState(AndroidSdkPackage::Available); + m_sdkModel->resetSelection(); + } + }); + + connect(m_ui->applySelectionButton, &QPushButton::clicked, + this, &AndroidSdkManagerWidget::onApplyButton); + connect(m_ui->cancelButton, &QPushButton::clicked, this, + &AndroidSdkManagerWidget::onCancel); + connect(m_ui->nativeSdkManagerButton, &QPushButton::clicked, + this, &AndroidSdkManagerWidget::onNativeSdkManager); +} + +AndroidSdkManagerWidget::~AndroidSdkManagerWidget() +{ + if (m_currentOperation) + delete m_currentOperation; + cancelPendingOperations(); + delete m_formatter; + delete m_ui; +} + +void AndroidSdkManagerWidget::setSdkManagerControlsEnabled(bool enable) +{ + m_ui->packagesTypeGroup->setEnabled(enable); + m_ui->expandCheck->setVisible(enable); + m_ui->warningIconLabel->setVisible(!enable); + m_ui->warningLabel->setVisible(!enable); + m_ui->packagesView->setEnabled(enable); + m_ui->updateInstalledButton->setEnabled(enable); +} + +void AndroidSdkManagerWidget::onApplyButton() +{ + QTC_ASSERT(currentView() == PackageListing, return); + + if (m_sdkManager->isBusy()) { + m_formatter->appendMessage(tr("\nSDK Manager is busy."), Utils::StdErrFormat); + return; + } + + const QList packagesToUpdate = m_sdkModel->userSelection(); + if (packagesToUpdate.isEmpty()) + return; + + QStringList installPackages, uninstallPackages, installSdkPaths, uninstallSdkPaths; + for (auto package : packagesToUpdate) { + QString str = QString(" %1").arg(package->descriptionText()); + if (package->state() == AndroidSdkPackage::Installed) { + uninstallSdkPaths << package->sdkStylePath(); + uninstallPackages << str; + } else { + installSdkPaths << package->sdkStylePath(); + installPackages << str; + } + } + + QMessageBox messageDlg(QMessageBox::Information, tr("Android SDK Changes"), + tr("%n Android SDK packages shall be updated.", + "", packagesToUpdate.count()), + QMessageBox::Ok | QMessageBox::Cancel, this); + + QString details; + if (!uninstallPackages.isEmpty()) + details = tr("[Packages to be uninstalled:]\n").append(uninstallPackages.join("\n")); + + if (!installPackages.isEmpty()) { + if (!uninstallPackages.isEmpty()) + details.append("\n\n"); + details.append("[Packages to be installed:]\n").append(installPackages.join("\n")); + } + messageDlg.setDetailedText(details); + if (messageDlg.exec() == QMessageBox::Cancel) + return; + + // User agreed with the selection. Begin packages install/uninstall + emit updatingSdk(); + switchView(Operations); + m_formatter->appendMessage(tr("Updating selected packages...\n"), + Utils::NormalMessageFormat); + m_formatter->appendMessage(tr("Closing the %1 dialog will cancel the running and scheduled SDK " + "operations.\n").arg(Utils::HostOsInfo::isMacHost() ? + tr("preferences") : tr("options")), + Utils::LogMessageFormat); + + addPackageFuture(m_sdkManager->update(installSdkPaths, uninstallSdkPaths)); +} + +void AndroidSdkManagerWidget::onUpdatePackages() +{ + if (m_sdkManager->isBusy()) { + m_formatter->appendMessage(tr("\nSDK Manager is busy."), Utils::StdErrFormat); + return; + } + switchView(Operations); + m_formatter->appendMessage(tr("Updating installed packages\n"), Utils::NormalMessageFormat); + addPackageFuture(m_sdkManager->updateAll()); +} + +void AndroidSdkManagerWidget::onCancel() +{ + cancelPendingOperations(); +} + +void AndroidSdkManagerWidget::onNativeSdkManager() +{ + if (m_androidConfig.useNativeUiTools()) { + QProcess::startDetached(m_androidConfig.androidToolPath().toString()); + } else { + QMessageBox::warning(this, tr("Native SDK Manager Not Available"), + tr("SDK manager UI tool is not available in the installed SDK tools" + "(version %1). Use the command line tool \"sdkmanager\" for " + "advanced SDK management.") + .arg(m_androidConfig.sdkToolsVersion().toString())); + } +} + +void AndroidSdkManagerWidget::onOperationResult(int index) +{ + QTC_ASSERT(m_currentOperation, return); + auto breakLine = [](const QString &line) { return line.endsWith("\n") ? line : line + "\n";}; + AndroidSdkManager::OperationOutput result = m_currentOperation->resultAt(index); + if (!result.stdError.isEmpty()) + m_formatter->appendMessage(breakLine(result.stdError), Utils::StdErrFormat); + if (!result.stdOutput.isEmpty()) + m_formatter->appendMessage(breakLine(result.stdOutput), Utils::StdOutFormat); +} + +void AndroidSdkManagerWidget::addPackageFuture(const QFuture + &future) +{ + QTC_ASSERT(!m_currentOperation, return); + if (!future.isFinished() || !future.isCanceled()) { + m_currentOperation = new QFutureWatcher; + m_currentOperation->setFuture(future); + connect(m_currentOperation, + &QFutureWatcher::resultReadyAt, + this, &AndroidSdkManagerWidget::onOperationResult); + connect(m_currentOperation, &QFutureWatcher::finished, + this, &AndroidSdkManagerWidget::packageFutureFinished); + connect(m_currentOperation, + &QFutureWatcher::progressValueChanged, + [this](int value) { + m_ui->operationProgress->setValue(value); + }); + } else { + qCDebug(androidSdkMgrUiLog) << "Operation canceled/finished before adding to the queue"; + if (m_sdkManager->isBusy()) { + m_formatter->appendMessage(tr("SDK Manager is busy. Operation cancelled."), + Utils::StdErrFormat); + } + notifyOperationFinished(); + } +} + +void AndroidSdkManagerWidget::notifyOperationFinished() +{ + if (!m_currentOperation || m_currentOperation->isFinished()) { + QMessageBox::information(this, tr("Android SDK Changes"), + tr("Android SDK operations finished."), QMessageBox::Ok); + switchView(PackageListing); + m_ui->operationProgress->setValue(0); + m_sdkManager->reloadPackages(true); + emit updatingSdkFinished(); + } +} + +void AndroidSdkManagerWidget::packageFutureFinished() +{ + if (!m_currentOperation) { + qCDebug(androidSdkMgrUiLog) << "Invalid State. No active operation."; + return; + } else if (m_currentOperation->isCanceled()) { + m_formatter->appendMessage(tr("Operation cancelled.\n"), Utils::StdErrFormat); + } + m_ui->operationProgress->setValue(100); + m_currentOperation->deleteLater(); + m_currentOperation = nullptr; + notifyOperationFinished(); +} + +void AndroidSdkManagerWidget::cancelPendingOperations() +{ + if (!m_sdkManager->isBusy()) { + m_formatter->appendMessage(tr("\nNo pending operations to cancel...\n"), + Utils::NormalMessageFormat); + return; + } + m_formatter->appendMessage(tr("\nCancelling pending operations...\n"), + Utils::NormalMessageFormat); + m_sdkManager->cancelOperatons(); +} + +void AndroidSdkManagerWidget::switchView(AndroidSdkManagerWidget::View view) +{ + m_ui->viewStack->setCurrentWidget(view == PackageListing ? + m_ui->packagesStack : m_ui->outputStack); + m_formatter->clear(); + m_ui->outputEdit->clear(); +} + +AndroidSdkManagerWidget::View AndroidSdkManagerWidget::currentView() const +{ + return m_ui->viewStack->currentWidget() == m_ui->packagesStack ? PackageListing : Operations; +} + +PackageFilterModel::PackageFilterModel(AndroidSdkModel *sdkModel) : + QSortFilterProxyModel(sdkModel) +{ + setSourceModel(sdkModel); +} + +void PackageFilterModel::setAcceptedPackageState(AndroidSdkPackage::PackageState state) +{ + m_packageState = state; + invalidateFilter(); +} + +bool PackageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex srcIndex = sourceModel()->index(sourceRow, 0, sourceParent); + if (!srcIndex.isValid()) + return false; + + auto packageState = [](const QModelIndex& i) { + return (AndroidSdkPackage::PackageState)i.data(AndroidSdkModel::PackageStateRole).toInt(); + }; + + bool showTopLevel = false; + if (!sourceParent.isValid()) { + // Top Level items + for (int row = 0; row < sourceModel()->rowCount(srcIndex); ++row) { + QModelIndex childIndex = sourceModel()->index(row, 0, srcIndex); + if (m_packageState & packageState(childIndex)) { + showTopLevel = true; + break; + } + } + } + + return showTopLevel || (packageState(srcIndex) & m_packageState); +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidsdkmanagerwidget.h b/src/plugins/android/androidsdkmanagerwidget.h new file mode 100644 index 00000000000..ea96072d1bc --- /dev/null +++ b/src/plugins/android/androidsdkmanagerwidget.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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 +#include + +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 &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 *m_currentOperation = nullptr; +}; + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidsdkmanagerwidget.ui b/src/plugins/android/androidsdkmanagerwidget.ui new file mode 100644 index 00000000000..19cbb740df6 --- /dev/null +++ b/src/plugins/android/androidsdkmanagerwidget.ui @@ -0,0 +1,272 @@ + + + Android::Internal::AndroidSdkManagerWidget + + + + 0 + 0 + 664 + 396 + + + + Android SDK Manager + + + + -1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 4 + + + + + + + + 0 + 0 + + + + Expand All + + + true + + + + + + + + + + + + + + + 0 + 0 + + + + SDK manger is not available with the current version of SDK tools. Use native SDK manager. + + + + + + + + + 20 + + + false + + + + + + + + + Update Installed + + + + + + + false + + + Apply + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Show Packages + + + + 12 + + + + + Available + + + + + + + Installed + + + + + + + All + + + true + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 5 + + + + + + + + Native SDK Manager... + + + + + + + + + + + + + Cancel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + + + + + 100 + + + 0 + + + true + + + false + + + + + + + + + + + + Utils::ElidingLabel + QLabel +
utils/elidinglabel.h
+
+
+ + packagesView + showAllRadio + showInstalledRadio + showAvailableRadio + outputEdit + + + +
diff --git a/src/plugins/android/androidsdkmodel.cpp b/src/plugins/android/androidsdkmodel.cpp new file mode 100644 index 00000000000..8e84544ac74 --- /dev/null +++ b/src/plugins/android/androidsdkmodel.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ +#include "androidsdkmodel.h" +#include "androidmanager.h" +#include "androidsdkmanager.h" + +#include "utils/algorithm.h" +#include "utils/qtcassert.h" +#include "utils/utilsicons.h" + +#include + +namespace Android { +namespace Internal { + +const int packageColCount = 4; + +AndroidSdkModel::AndroidSdkModel(AndroidSdkManager *sdkManager, QObject *parent) + : QAbstractItemModel(parent), + m_sdkManager(sdkManager) +{ + QTC_CHECK(m_sdkManager); + connect(m_sdkManager, &AndroidSdkManager::packageReloadBegin, [this]() { + clearContainers(); + beginResetModel(); + }); + connect(m_sdkManager, &AndroidSdkManager::packageReloadFinished, [this]() { + refreshData(); + endResetModel(); + }); +} + +QVariant AndroidSdkModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(orientation) + QVariant data; + if (role == Qt::DisplayRole) { + switch (section) { + case packageNameColumn: + data = tr("Package"); + break; + case packageRevisionColumn: + data = tr("Revision"); + break; + case apiLevelColumn: + data = tr("API"); + break; + case operationColumn: + data = tr("Operation"); + break; + default: + break; + } + } + return data; +} + +QModelIndex AndroidSdkModel::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid()) { + // Packages under top items. + if (parent.row() == 0) { + // Tools packages + if (row < m_tools.count()) + return createIndex(row, column, const_cast(m_tools.at(row))); + } else if (parent.row() < m_sdkPlatforms.count() + 1) { + // Platform packages + const SdkPlatform *sdkPlatform = m_sdkPlatforms.at(parent.row() - 1); + SystemImageList images = sdkPlatform->systemImages(AndroidSdkPackage::AnyValidState); + if (row < images.count() + 1) { + if (row == 0) + return createIndex(row, column, const_cast(sdkPlatform)); + else + return createIndex(row, column, images.at(row - 1)); + } + } + } else if (row < m_sdkPlatforms.count() + 1) { + return createIndex(row, column); // Top level items (Tools & platform) + } + + return QModelIndex(); +} + +QModelIndex AndroidSdkModel::parent(const QModelIndex &index) const +{ + void *ip = index.internalPointer(); + if (!ip) + return QModelIndex(); + + auto package = static_cast(ip); + if (package->type() == AndroidSdkPackage::SystemImagePackage) { + auto image = static_cast(package); + int row = m_sdkPlatforms.indexOf(const_cast(image->platform())); + if (row > -1) + return createIndex(row + 1, 0); + } else if (package->type() == AndroidSdkPackage::SdkPlatformPackage) { + int row = m_sdkPlatforms.indexOf(static_cast(package)); + if (row > -1) + return createIndex(row + 1, 0); + } else { + return createIndex(0, 0); // Tools + } + + return QModelIndex(); +} + +int AndroidSdkModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return m_sdkPlatforms.count() + 1; + + if (!parent.internalPointer()) { + if (parent.row() == 0) // Tools + return m_tools.count(); + + if (parent.row() <= m_sdkPlatforms.count()) { + const SdkPlatform * platform = m_sdkPlatforms.at(parent.row() - 1); + return platform->systemImages(AndroidSdkPackage::AnyValidState).count() + 1; + } + } + + return 0; +} + +int AndroidSdkModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return packageColCount; +} + +QVariant AndroidSdkModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + + if (!index.parent().isValid()) { + // Top level tools + if (index.row() == 0) { + return role == Qt::DisplayRole && index.column() == packageNameColumn ? + QVariant(tr("Tools")) : QVariant(); + } + // Top level platforms + const SdkPlatform *platform = m_sdkPlatforms.at(index.row() - 1); + if (role == Qt::DisplayRole) { + if (index.column() == packageNameColumn) { + QString androidName = AndroidManager::androidNameForApiLevel(platform->apiLevel()); + if (androidName.startsWith("Android")) + return androidName; + else + return platform->displayText(); + } else if (index.column() == apiLevelColumn) { + return platform->apiLevel(); + } + } + return QVariant(); + } + + auto p = static_cast(index.internalPointer()); + QString apiLevelStr; + if (p->type() == AndroidSdkPackage::SdkPlatformPackage) + apiLevelStr = QString::number(static_cast(p)->apiLevel()); + + if (p->type() == AndroidSdkPackage::SystemImagePackage) + apiLevelStr = QString::number(static_cast(p)->platform()->apiLevel()); + + if (role == Qt::DisplayRole) { + switch (index.column()) { + case packageNameColumn: + return p->type() == AndroidSdkPackage::SdkPlatformPackage ? + tr("SDK Platform") : p->displayText(); + case packageRevisionColumn: + return p->revision().toString(); + case apiLevelColumn: + return apiLevelStr; + case operationColumn: + if (p->type() == AndroidSdkPackage::SdkToolsPackage && + p->state() == AndroidSdkPackage::Installed) { + return tr("Update Only"); + } else { + return p->state() == AndroidSdkPackage::Installed ? tr("Uninstall") : tr("Install"); + } + default: + break; + } + } + + if (role == Qt::DecorationRole && index.column() == packageNameColumn) { + return p->state() == AndroidSdkPackage::Installed ? Utils::Icons::OK.icon() : + Utils::Icons::EMPTY16.icon(); + } + + if (role == Qt::CheckStateRole && index.column() == operationColumn ) + return m_changeState.contains(p) ? Qt::Checked : Qt::Unchecked; + + if (role == Qt::ToolTipRole) + return QString("%1 - (%2)").arg(p->descriptionText()).arg(p->sdkStylePath()); + + if (role == PackageTypeRole) + return p->type(); + + if (role == PackageStateRole) + return p->state(); + + return QVariant(); +} + +QHash AndroidSdkModel::roleNames() const +{ + QHash roles; + roles[PackageTypeRole] = "PackageRole"; + roles[PackageStateRole] = "PackageState"; + return roles; +} + +Qt::ItemFlags AndroidSdkModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags f = QAbstractItemModel::flags(index); + if (index.column() == operationColumn) + f |= Qt::ItemIsUserCheckable; + + void *ip = index.internalPointer(); + if (ip && index.column() == operationColumn) { + auto package = static_cast(ip); + if (package->state() == AndroidSdkPackage::Installed && + package->type() == AndroidSdkPackage::SdkToolsPackage) { + f &= ~Qt::ItemIsEnabled; + } + } + return f; +} + +bool AndroidSdkModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + void *ip = index.internalPointer(); + if (ip && role == Qt::CheckStateRole) { + auto package = static_cast(ip); + if (value.toInt() == Qt::Checked) { + m_changeState << package; + emit dataChanged(index, index, {Qt::CheckStateRole}); + } else if (m_changeState.remove(package)) { + emit dataChanged(index, index, {Qt::CheckStateRole}); + } + return true; + } + return false; +} + +QList AndroidSdkModel::userSelection() const +{ + return m_changeState.toList(); +} + +void AndroidSdkModel::resetSelection() +{ + beginResetModel(); + m_changeState.clear(); + endResetModel(); +} + +void AndroidSdkModel::clearContainers() +{ + m_sdkPlatforms.clear(); + m_tools.clear(); + m_changeState.clear(); +} + +void AndroidSdkModel::refreshData() +{ + clearContainers(); + for (AndroidSdkPackage *p : m_sdkManager->allSdkPackages()) { + if (p->type() == AndroidSdkPackage::SdkPlatformPackage) + m_sdkPlatforms << static_cast(p); + else + m_tools << p; + } + Utils::sort(m_sdkPlatforms, [](const SdkPlatform *p1, const SdkPlatform *p2) { + return p1->apiLevel() > p2->apiLevel(); + }); + + Utils::sort(m_tools, [](const AndroidSdkPackage *p1, const AndroidSdkPackage *p2) { + if (p1->state() == p2->state()) { + if (p1->type() == p2->type()) + return p1->revision() > p2->revision(); + else + return p1->type() > p2->type(); + } else { + return p1->state() < p2->state(); + } + }); +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidsdkmodel.h b/src/plugins/android/androidsdkmodel.h new file mode 100644 index 00000000000..a22500e16cc --- /dev/null +++ b/src/plugins/android/androidsdkmodel.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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 + +#include + +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 roleNames() const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + + QList userSelection() const; + void resetSelection(); + +private: + void clearContainers(); + void refreshData(); + +private: + AndroidSdkManager *m_sdkManager; + QList m_sdkPlatforms; + QList m_tools; + QSet m_changeState; +}; + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidsdkpackage.cpp b/src/plugins/android/androidsdkpackage.cpp index 44174cee906..b7d15105854 100644 --- a/src/plugins/android/androidsdkpackage.cpp +++ b/src/plugins/android/androidsdkpackage.cpp @@ -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); } diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 3842b15e692..443c3a29fcf 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -33,6 +33,7 @@ #include "androidavdmanager.h" #include "androidsdkmanager.h" #include "avddialog.h" +#include "androidsdkmanagerwidget.h" #include #include @@ -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 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 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 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(m_ui->javaDetailsWidget->widget()); auto androidSummaryWidget = static_cast(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(); } diff --git a/src/plugins/android/androidsettingswidget.h b/src/plugins/android/androidsettingswidget.h index 5d81ad8333f..a5e42384386 100644 --- a/src/plugins/android/androidsettingswidget.h +++ b/src/plugins/android/androidsettingswidget.h @@ -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 m_futureWatcher; diff --git a/src/plugins/android/androidsettingswidget.ui b/src/plugins/android/androidsettingswidget.ui index 878c1d95ee7..4f164010153 100644 --- a/src/plugins/android/androidsettingswidget.ui +++ b/src/plugins/android/androidsettingswidget.ui @@ -6,14 +6,26 @@ 0 0 - 938 - 728 + 1131 + 826 Android Configuration + + 4 + + + 4 + + + 4 + + + 4 + @@ -175,122 +187,167 @@ - - - QFrame::StyledPanel + + + 0 - - QFrame::Raised - - - - - - false - - - Start... - - - - - - - - - AVD Manager - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - System/data partition size: - - - - - - - Mb - - - 99999 - - - 1024 - - - - - - - Start AVD Manager... - - - - - - - - - false - - - Remove - - - - - - - Add... - - - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - Qt::ElideMiddle - - - false - - - - - - - Qt::Vertical - - - - 20 - 129 - - - - - + + + AVD Manager + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideMiddle + + + false + + + + + + + + + System/data partition size: + + + + + + + + 0 + 0 + + + + Mb + + + 99999 + + + 1024 + + + + + + + + 0 + 0 + + + + Add... + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 8 + + + + + + + + false + + + + 0 + 0 + + + + Remove + + + + + + + false + + + + 0 + 0 + + + + Start... + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Native AVD Manager... + + + + + + + + + + SDK Manager + + @@ -299,7 +356,7 @@ Utils::PathChooser QWidget -
utils/pathchooser.h
+
utils/pathchooser.h
1
From c52ee5238bdd97ef5838043c4428ee831b046ccc Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Wed, 27 Sep 2017 15:26:59 +0200 Subject: [PATCH 22/27] Android: Enable common arguments for SDK manager command Task-number: QTCREATORBUG-18978 Change-Id: I9f3e2b16a77ff949268960d2b5c5aa1896fa3770 Reviewed-by: BogDan Vatra Reviewed-by: Leena Miettinen --- src/plugins/android/androidconfigurations.cpp | 15 ++++ src/plugins/android/androidconfigurations.h | 3 + src/plugins/android/androidsdkmanager.cpp | 43 +++++++-- src/plugins/android/androidsdkmanager.h | 3 +- .../android/androidsdkmanagerwidget.cpp | 87 ++++++++++++++++++- src/plugins/android/androidsdkmanagerwidget.h | 5 +- .../android/androidsdkmanagerwidget.ui | 7 ++ 7 files changed, 153 insertions(+), 10 deletions(-) diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index ca9c39a743a..a44f605780e 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -87,6 +87,7 @@ namespace { const QLatin1String SettingsGroup("AndroidConfigurations"); const QLatin1String SDKLocationKey("SDKLocation"); + const QLatin1String SDKManagerToolArgsKey("SDKManagerToolArgs"); const QLatin1String NDKLocationKey("NDKLocation"); const QLatin1String OpenJDKLocationKey("OpenJDKLocation"); const QLatin1String KeystoreLocationKey("KeystoreLocation"); @@ -246,6 +247,7 @@ void AndroidConfig::load(const QSettings &settings) // user settings m_partitionSize = settings.value(PartitionSizeKey, 1024).toInt(); m_sdkLocation = FileName::fromString(settings.value(SDKLocationKey).toString()); + m_sdkManagerToolArgs = settings.value(SDKManagerToolArgsKey).toStringList(); m_ndkLocation = FileName::fromString(settings.value(NDKLocationKey).toString()); m_openJDKLocation = FileName::fromString(settings.value(OpenJDKLocationKey).toString()); m_keystoreLocation = FileName::fromString(settings.value(KeystoreLocationKey).toString()); @@ -261,6 +263,7 @@ void AndroidConfig::load(const QSettings &settings) && settings.value(changeTimeStamp).toInt() != QFileInfo(sdkSettingsFileName()).lastModified().toMSecsSinceEpoch() / 1000) { // persisten settings m_sdkLocation = FileName::fromString(reader.restoreValue(SDKLocationKey, m_sdkLocation.toString()).toString()); + m_sdkManagerToolArgs = reader.restoreValue(SDKManagerToolArgsKey, m_sdkManagerToolArgs).toStringList(); m_ndkLocation = FileName::fromString(reader.restoreValue(NDKLocationKey, m_ndkLocation.toString()).toString()); m_openJDKLocation = FileName::fromString(reader.restoreValue(OpenJDKLocationKey, m_openJDKLocation.toString()).toString()); m_keystoreLocation = FileName::fromString(reader.restoreValue(KeystoreLocationKey, m_keystoreLocation.toString()).toString()); @@ -283,6 +286,7 @@ void AndroidConfig::save(QSettings &settings) const // user settings settings.setValue(SDKLocationKey, m_sdkLocation.toString()); + settings.setValue(SDKManagerToolArgsKey, m_sdkManagerToolArgs); settings.setValue(NDKLocationKey, m_ndkLocation.toString()); settings.setValue(OpenJDKLocationKey, m_openJDKLocation.toString()); settings.setValue(KeystoreLocationKey, m_keystoreLocation.toString()); @@ -727,6 +731,17 @@ QVersionNumber AndroidConfig::buildToolsVersion() const return maxVersion; } + +QStringList AndroidConfig::sdkManagerToolArgs() const +{ + return m_sdkManagerToolArgs; +} + +void AndroidConfig::setSdkManagerToolArgs(const QStringList &args) +{ + m_sdkManagerToolArgs = args; +} + FileName AndroidConfig::ndkLocation() const { return m_ndkLocation; diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 0adcc8d2d36..4565d5e3635 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -100,6 +100,8 @@ public: void setSdkLocation(const Utils::FileName &sdkLocation); QVersionNumber sdkToolsVersion() const; QVersionNumber buildToolsVersion() const; + QStringList sdkManagerToolArgs() const; + void setSdkManagerToolArgs(const QStringList &args); Utils::FileName ndkLocation() const; QVersionNumber ndkVersion() const; @@ -166,6 +168,7 @@ private: void updateNdkInformation() const; Utils::FileName m_sdkLocation; + QStringList m_sdkManagerToolArgs; Utils::FileName m_ndkLocation; Utils::FileName m_openJDKLocation; Utils::FileName m_keystoreLocation; diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index b8dcfc9f620..819c4902755 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "androidsdkmanager.h" +#include "androidconstants.h" #include "androidmanager.h" #include "androidtoolmanager.h" @@ -31,7 +32,6 @@ #include "utils/qtcassert.h" #include "utils/runextensions.h" #include "utils/synchronousprocess.h" -#include "utils/environment.h" #include #include @@ -52,6 +52,7 @@ const QVersionNumber sdkManagerIntroVersion(25, 3 ,0); const char installLocationKey[] = "Installed Location:"; const char revisionKey[] = "Version:"; const char descriptionKey[] = "Description:"; +const char commonArgsKey[] = "Common Arguments:"; const int sdkManagerCmdTimeoutS = 60; const int sdkManagerOperationTimeoutS = 600; @@ -178,6 +179,7 @@ public: const AndroidSdkPackageList &allPackages(bool forceUpdate = false); void refreshSdkPackages(bool forceReload = false); + void parseCommonArguments(QFutureInterface &fi); void updateInstalled(SdkCmdFutureInterface &fi); void update(SdkCmdFutureInterface &fi, const QStringList &install, const QStringList &uninstall); @@ -335,6 +337,11 @@ bool AndroidSdkManager::isBusy() const return m_d->m_activeOperation && !m_d->m_activeOperation->isFinished(); } +QFuture AndroidSdkManager::availableArguments() const +{ + return Utils::runAsync(&AndroidSdkManagerPrivate::parseCommonArguments, m_d.get()); +} + QFuture AndroidSdkManager::updateAll() { if (isBusy()) { @@ -663,7 +670,6 @@ AndroidSdkManagerPrivate::AndroidSdkManagerPrivate(AndroidSdkManager &sdkManager m_sdkManager(sdkManager), m_config(config) { - } AndroidSdkManagerPrivate::~AndroidSdkManagerPrivate() @@ -709,8 +715,9 @@ void AndroidSdkManagerPrivate::reloadSdkPackages() m_allPackages = Utils::transform(toolManager.availableSdkPlatforms(), toAndroidSdkPackages); } else { QString packageListing; - if (sdkManagerCommand(m_config.sdkManagerToolPath(), QStringList({"--list", "--verbose"}), - &packageListing)) { + QStringList args({"--list", "--verbose"}); + args << m_config.sdkManagerToolArgs(); + if (sdkManagerCommand(m_config.sdkManagerToolPath(), args, &packageListing)) { SdkManagerOutputParser parser(m_allPackages); parser.parsePackageListing(packageListing); } @@ -736,6 +743,7 @@ void AndroidSdkManagerPrivate::updateInstalled(SdkCmdFutureInterface &fi) "Updating installed packages."); fi.reportResult(result); QStringList args("--update"); + args << m_config.sdkManagerToolArgs(); if (!fi.isCanceled()) sdkManagerCommand(m_config.sdkManagerToolPath(), args, m_sdkManager, fi, result, 100); else @@ -785,13 +793,17 @@ void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringLi // Uninstall packages for (const QString &sdkStylePath : uninstall) { // Uninstall operations are not interptible. We don't want to leave half uninstalled. - if (doOperation(sdkStylePath, {"--uninstall", sdkStylePath}, false)) + QStringList args; + args << "--uninstall" << sdkStylePath << m_config.sdkManagerToolArgs(); + if (doOperation(sdkStylePath, args, false)) break; } // Install packages for (const QString &sdkStylePath : install) { - if (doOperation(sdkStylePath, {sdkStylePath}, true)) + QStringList args(sdkStylePath); + args << m_config.sdkManagerToolArgs(); + if (doOperation(sdkStylePath, args, true)) break; } fi.setProgressValue(100); @@ -805,6 +817,25 @@ void AndroidSdkManagerPrivate::addWatcher(const QFuturesetFuture(future); } +void AndroidSdkManagerPrivate::parseCommonArguments(QFutureInterface &fi) +{ + QString argumentDetails; + QString output; + sdkManagerCommand(m_config.sdkManagerToolPath(), QStringList("--help"), &output); + bool foundTag = false; + for (const QString& line : output.split('\n')) { + if (fi.isCanceled()) + break; + if (foundTag) + argumentDetails.append(line + "\n"); + else if (line.startsWith(commonArgsKey)) + foundTag = true; + } + + if (!fi.isCanceled()) + fi.reportResult(argumentDetails); +} + void AndroidSdkManagerPrivate::clearPackages() { for (AndroidSdkPackage *p : m_allPackages) diff --git a/src/plugins/android/androidsdkmanager.h b/src/plugins/android/androidsdkmanager.h index fc41fb594ed..db2316aee82 100644 --- a/src/plugins/android/androidsdkmanager.h +++ b/src/plugins/android/androidsdkmanager.h @@ -73,8 +73,9 @@ public: AndroidSdkPackage::PackageState state = AndroidSdkPackage::Installed); void reloadPackages(bool forceReload = false); - bool isBusy() const; + + QFuture availableArguments() const; QFuture updateAll(); QFuture update(const QStringList &install, const QStringList &uninstall); diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp index 42b0234610f..30929f8bf57 100644 --- a/src/plugins/android/androidsdkmanagerwidget.cpp +++ b/src/plugins/android/androidsdkmanagerwidget.cpp @@ -26,13 +26,18 @@ #include "ui_androidsdkmanagerwidget.h" #include "androidconfigurations.h" +#include "androidsdkmanager.h" #include "androidsdkmodel.h" +#include "utils/runextensions.h" #include "utils/outputformatter.h" #include "utils/runextensions.h" #include "utils/qtcassert.h" #include "utils/utilsicons.h" +#include +#include +#include #include #include #include @@ -47,6 +52,21 @@ namespace Internal { using namespace std::placeholders; +class OptionsDialog : public QDialog +{ +public: + OptionsDialog(AndroidSdkManager *sdkManager, const QStringList &args, + QWidget *parent = nullptr); + ~OptionsDialog(); + + QStringList sdkManagerArguments() const; + +private: + QPlainTextEdit *argumentDetailsEdit; + QLineEdit *argumentsEdit; + QFuture m_optionsFuture; +}; + class PackageFilterModel : public QSortFilterProxyModel { public: @@ -59,7 +79,7 @@ private: AndroidSdkPackage::PackageState m_packageState = AndroidSdkPackage::AnyValidState; }; -AndroidSdkManagerWidget::AndroidSdkManagerWidget(const AndroidConfig &config, +AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config, AndroidSdkManager *sdkManager, QWidget *parent) : QWidget(parent), m_androidConfig(config), @@ -67,6 +87,7 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(const AndroidConfig &config, m_sdkModel(new AndroidSdkModel(m_sdkManager, this)), m_ui(new Ui::AndroidSdkManagerWidget) { + QTC_CHECK(sdkManager); m_ui->setupUi(this); m_ui->warningLabel->setElideMode(Qt::ElideRight); m_ui->warningIconLabel->setPixmap(Utils::Icons::WARNING.pixmap()); @@ -128,6 +149,8 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(const AndroidConfig &config, &AndroidSdkManagerWidget::onCancel); connect(m_ui->nativeSdkManagerButton, &QPushButton::clicked, this, &AndroidSdkManagerWidget::onNativeSdkManager); + connect(m_ui->optionsButton, &QPushButton::clicked, + this, &AndroidSdkManagerWidget::onSdkManagerOptions); } AndroidSdkManagerWidget::~AndroidSdkManagerWidget() @@ -147,6 +170,7 @@ void AndroidSdkManagerWidget::setSdkManagerControlsEnabled(bool enable) m_ui->warningLabel->setVisible(!enable); m_ui->packagesView->setEnabled(enable); m_ui->updateInstalledButton->setEnabled(enable); + m_ui->optionsButton->setEnabled(enable); } void AndroidSdkManagerWidget::onApplyButton() @@ -323,6 +347,18 @@ AndroidSdkManagerWidget::View AndroidSdkManagerWidget::currentView() const return m_ui->viewStack->currentWidget() == m_ui->packagesStack ? PackageListing : Operations; } +void AndroidSdkManagerWidget::onSdkManagerOptions() +{ + OptionsDialog dlg(m_sdkManager, m_androidConfig.sdkManagerToolArgs(), this); + if (dlg.exec() == QDialog::Accepted) { + QStringList arguments = dlg.sdkManagerArguments(); + if (arguments != m_androidConfig.sdkManagerToolArgs()) { + m_androidConfig.setSdkManagerToolArgs(arguments); + m_sdkManager->reloadPackages(true); + } + } +} + PackageFilterModel::PackageFilterModel(AndroidSdkModel *sdkModel) : QSortFilterProxyModel(sdkModel) { @@ -360,5 +396,54 @@ bool PackageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour return showTopLevel || (packageState(srcIndex) & m_packageState); } +OptionsDialog::OptionsDialog(AndroidSdkManager *sdkManager, const QStringList &args, + QWidget *parent) : QDialog(parent) +{ + QTC_CHECK(sdkManager); + resize(800, 480); + setWindowTitle(tr("SDK Manager Arguments")); + + argumentDetailsEdit = new QPlainTextEdit(this); + argumentDetailsEdit->setReadOnly(true); + + auto populateOptions = [this](const QString& options) { + if (options.isEmpty()) { + argumentDetailsEdit->setPlainText(tr("Cannot load available arguments for " + "\"sdkmanager\" command.")); + } else { + argumentDetailsEdit->setPlainText(options); + } + }; + m_optionsFuture = sdkManager->availableArguments(); + Utils::onResultReady(m_optionsFuture, populateOptions); + + auto dialogButtons = new QDialogButtonBox(this); + dialogButtons->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + connect(dialogButtons, &QDialogButtonBox::accepted, this, &OptionsDialog::accept); + connect(dialogButtons, &QDialogButtonBox::rejected, this, &OptionsDialog::reject); + + argumentsEdit = new QLineEdit(this); + argumentsEdit->setText(args.join(" ")); + + auto gridLayout = new QGridLayout(this); + gridLayout->addWidget(new QLabel(tr("SDK manager arguments:"), this), 0, 0, 1, 1); + gridLayout->addWidget(argumentsEdit, 0, 1, 1, 1); + gridLayout->addWidget(new QLabel(tr("Available arguments:"), this), 1, 0, 1, 2); + gridLayout->addWidget(argumentDetailsEdit, 2, 0, 1, 2); + gridLayout->addWidget(dialogButtons, 3, 0, 1, 2); +} + +OptionsDialog::~OptionsDialog() +{ + m_optionsFuture.cancel(); + m_optionsFuture.waitForFinished(); +} + +QStringList OptionsDialog::sdkManagerArguments() const +{ + QString userInput = argumentsEdit->text().simplified(); + return userInput.isEmpty() ? QStringList() : userInput.split(' '); +} + } // namespace Internal } // namespace Android diff --git a/src/plugins/android/androidsdkmanagerwidget.h b/src/plugins/android/androidsdkmanagerwidget.h index ea96072d1bc..8b539a3598f 100644 --- a/src/plugins/android/androidsdkmanagerwidget.h +++ b/src/plugins/android/androidsdkmanagerwidget.h @@ -52,7 +52,7 @@ class AndroidSdkManagerWidget : public QWidget }; public: - AndroidSdkManagerWidget(const AndroidConfig &config, AndroidSdkManager *sdkManager, + AndroidSdkManagerWidget(AndroidConfig &config, AndroidSdkManager *sdkManager, QWidget *parent = nullptr); ~AndroidSdkManagerWidget(); @@ -68,6 +68,7 @@ private: void onCancel(); void onNativeSdkManager(); void onOperationResult(int index); + void onSdkManagerOptions(); void addPackageFuture(const QFuture &future); void notifyOperationFinished(); void packageFutureFinished(); @@ -75,7 +76,7 @@ private: void switchView(View view); View currentView() const; - const AndroidConfig &m_androidConfig; + AndroidConfig &m_androidConfig; AndroidSdkManager *m_sdkManager = nullptr; AndroidSdkModel *m_sdkModel = nullptr; Ui::AndroidSdkManagerWidget *m_ui = nullptr; diff --git a/src/plugins/android/androidsdkmanagerwidget.ui b/src/plugins/android/androidsdkmanagerwidget.ui index 19cbb740df6..ac5b8526663 100644 --- a/src/plugins/android/androidsdkmanagerwidget.ui +++ b/src/plugins/android/androidsdkmanagerwidget.ui @@ -175,6 +175,13 @@ + + + + Advanced Options... + + + From 50023725330c10d71042dadc76a2129e06a906de Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Tue, 26 Sep 2017 11:24:16 +0200 Subject: [PATCH 23/27] Android: Auto installation of missing necessary SDK packages Task-number: QTCREATORBUG-18978 Change-Id: Iac3e2c956ac3da717c6509f28b8d810827947ea0 Reviewed-by: BogDan Vatra --- .../android/androidsdkmanagerwidget.cpp | 8 +++- src/plugins/android/androidsdkmanagerwidget.h | 1 + src/plugins/android/androidsdkmodel.cpp | 38 ++++++++++++++++++- src/plugins/android/androidsdkmodel.h | 5 ++- src/plugins/android/androidsettingswidget.cpp | 28 +++++++++++++- src/plugins/android/androidsettingswidget.h | 1 + 6 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp index 30929f8bf57..35a3685d578 100644 --- a/src/plugins/android/androidsdkmanagerwidget.cpp +++ b/src/plugins/android/androidsdkmanagerwidget.cpp @@ -84,7 +84,7 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config, QWidget(parent), m_androidConfig(config), m_sdkManager(sdkManager), - m_sdkModel(new AndroidSdkModel(m_sdkManager, this)), + m_sdkModel(new AndroidSdkModel(m_androidConfig, m_sdkManager, this)), m_ui(new Ui::AndroidSdkManagerWidget) { QTC_CHECK(sdkManager); @@ -173,6 +173,12 @@ void AndroidSdkManagerWidget::setSdkManagerControlsEnabled(bool enable) m_ui->optionsButton->setEnabled(enable); } +void AndroidSdkManagerWidget::installEssentials() +{ + m_sdkModel->selectMissingEssentials(); + m_ui->applySelectionButton->click(); +} + void AndroidSdkManagerWidget::onApplyButton() { QTC_ASSERT(currentView() == PackageListing, return); diff --git a/src/plugins/android/androidsdkmanagerwidget.h b/src/plugins/android/androidsdkmanagerwidget.h index 8b539a3598f..c111fa0b968 100644 --- a/src/plugins/android/androidsdkmanagerwidget.h +++ b/src/plugins/android/androidsdkmanagerwidget.h @@ -57,6 +57,7 @@ public: ~AndroidSdkManagerWidget(); void setSdkManagerControlsEnabled(bool enable); + void installEssentials(); signals: void updatingSdk(); diff --git a/src/plugins/android/androidsdkmodel.cpp b/src/plugins/android/androidsdkmodel.cpp index 8e84544ac74..1b3b1a5af38 100644 --- a/src/plugins/android/androidsdkmodel.cpp +++ b/src/plugins/android/androidsdkmodel.cpp @@ -37,8 +37,10 @@ namespace Internal { const int packageColCount = 4; -AndroidSdkModel::AndroidSdkModel(AndroidSdkManager *sdkManager, QObject *parent) +AndroidSdkModel::AndroidSdkModel(const AndroidConfig &config, AndroidSdkManager *sdkManager, + QObject *parent) : QAbstractItemModel(parent), + m_config(config), m_sdkManager(sdkManager) { QTC_CHECK(m_sdkManager); @@ -268,6 +270,40 @@ bool AndroidSdkModel::setData(const QModelIndex &index, const QVariant &value, i return false; } +void AndroidSdkModel::selectMissingEssentials() +{ + resetSelection(); + bool selectPlatformTool = !m_config.adbToolPath().exists(); + bool selectBuildTools = m_config.buildToolsVersion().isNull(); + auto addTool = [this](QList::const_iterator itr) { + m_changeState << *itr; + auto i = index(std::distance(m_tools.cbegin(), itr), 0, index(0, 0)); + emit dataChanged(i, i, {Qt::CheckStateRole}); + }; + for (auto tool = m_tools.cbegin(); tool != m_tools.cend(); ++tool) { + if (selectPlatformTool && (*tool)->type() == AndroidSdkPackage::PlatformToolsPackage) { + // Select Platform tools + addTool(tool); + selectPlatformTool = false; + } + if (selectBuildTools && (*tool)->type() == AndroidSdkPackage::BuildToolsPackage) { + // Select build tools + addTool(tool); + selectBuildTools = false; + } + if (!selectPlatformTool && !selectBuildTools) + break; + } + + // Select SDK platform + if (m_sdkManager->installedSdkPlatforms().isEmpty() && !m_sdkPlatforms.isEmpty()) { + auto i = index(0, 0, index(1,0)); + m_changeState << m_sdkPlatforms.at(0); + emit dataChanged(i , i, {Qt::CheckStateRole}); + } +} + + QList AndroidSdkModel::userSelection() const { return m_changeState.toList(); diff --git a/src/plugins/android/androidsdkmodel.h b/src/plugins/android/androidsdkmodel.h index a22500e16cc..8c36b442efe 100644 --- a/src/plugins/android/androidsdkmodel.h +++ b/src/plugins/android/androidsdkmodel.h @@ -51,7 +51,8 @@ public: PackageStateRole }; - explicit AndroidSdkModel(AndroidSdkManager *sdkManager, QObject *parent = 0); + explicit AndroidSdkModel(const AndroidConfig &config, AndroidSdkManager *sdkManager, + QObject *parent = 0); // QAbstractItemModel overrides. QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; @@ -65,6 +66,7 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; + void selectMissingEssentials(); QList userSelection() const; void resetSelection(); @@ -73,6 +75,7 @@ private: void refreshData(); private: + const AndroidConfig &m_config; AndroidSdkManager *m_sdkManager; QList m_sdkPlatforms; QList m_tools; diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 443c3a29fcf..a198cc3cebd 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -295,7 +295,7 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent) connect(m_ui->NDKLocationPathChooser, &Utils::PathChooser::rawPathChanged, this, &AndroidSettingsWidget::validateNdk); connect(m_ui->SDKLocationPathChooser, &Utils::PathChooser::rawPathChanged, - this, &AndroidSettingsWidget::validateSdk); + this, &AndroidSettingsWidget::onSdkPathChanged); connect(m_ui->OpenJDKLocationPathChooser, &Utils::PathChooser::rawPathChanged, this, &AndroidSettingsWidget::validateJdk); connect(m_ui->AVDAddPushButton, &QAbstractButton::clicked, @@ -412,11 +412,16 @@ void AndroidSettingsWidget::validateNdk() updateUI(); } -void AndroidSettingsWidget::validateSdk() +void AndroidSettingsWidget::onSdkPathChanged() { auto sdkPath = Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath()); m_androidConfig.setSdkLocation(sdkPath); + // Package reload will trigger validateSdk. + m_sdkManager->reloadPackages(); +} +void AndroidSettingsWidget::validateSdk() +{ auto summaryWidget = static_cast(m_ui->androidDetailsWidget->widget()); summaryWidget->setPointValid(SdkPathExistsRow, m_androidConfig.sdkLocation().exists()); summaryWidget->setPointValid(SdkToolsInstalledRow, @@ -425,9 +430,28 @@ void AndroidSettingsWidget::validateSdk() m_androidConfig.adbToolPath().exists()); summaryWidget->setPointValid(BuildToolsInstalledRow, !m_androidConfig.buildToolsVersion().isNull()); + + // installedSdkPlatforms should not trigger a package reload as validate SDK is only called + // after AndroidSdkManager::packageReloadFinished. summaryWidget->setPointValid(PlatformSdkInstalledRow, !m_sdkManager->installedSdkPlatforms().isEmpty()); updateUI(); + bool sdkToolsOk = summaryWidget->rowsOk({SdkPathExistsRow, SdkToolsInstalledRow}); + bool componentsOk = summaryWidget->rowsOk({PlatformToolsInstalledRow, + BuildToolsInstalledRow, + PlatformSdkInstalledRow}); + + if (sdkToolsOk && !componentsOk && !m_androidConfig.useNativeUiTools()) { + // Ask user to install essential SDK components. Works only for sdk tools version >= 26.0.0 + QString message = tr("Android SDK installation is missing necessary packages. Do you " + "want to install the missing packages?"); + auto userInput = QMessageBox::information(this, tr("Missing Android SDK packages"), + message, QMessageBox::Yes | QMessageBox::No); + if (userInput == QMessageBox::Yes) { + m_ui->managerTabWidget->setCurrentWidget(m_ui->sdkManagerTab); + m_sdkManagerWidget->installEssentials(); + } + } } void AndroidSettingsWidget::openSDKDownloadUrl() diff --git a/src/plugins/android/androidsettingswidget.h b/src/plugins/android/androidsettingswidget.h index a5e42384386..8e22f511fa2 100644 --- a/src/plugins/android/androidsettingswidget.h +++ b/src/plugins/android/androidsettingswidget.h @@ -77,6 +77,7 @@ public: private: void validateJdk(); void validateNdk(); + void onSdkPathChanged(); void validateSdk(); void openSDKDownloadUrl(); void openNDKDownloadUrl(); From 35689db652820a3a2b4c6abea4df00514efea706 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Sat, 7 Oct 2017 23:22:29 +0200 Subject: [PATCH 24/27] Andriod: Check Android SDK pending licenses Pending licenses are checked before installing any package and the user is provided with an option to agree to license terms and conditions Task-number: QTCREATORBUG-19000 Change-Id: I4e149b4a6ac84c1f336bb7c50b0d62a2019c7868 Reviewed-by: BogDan Vatra Reviewed-by: Leena Miettinen --- src/plugins/android/androidsdkmanager.cpp | 174 ++++++++++++++- src/plugins/android/androidsdkmanager.h | 7 +- .../android/androidsdkmanagerwidget.cpp | 198 ++++++++++++++---- src/plugins/android/androidsdkmanagerwidget.h | 12 +- .../android/androidsdkmanagerwidget.ui | 76 +++++-- 5 files changed, 401 insertions(+), 66 deletions(-) diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index 819c4902755..4adf95401ea 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -32,9 +32,11 @@ #include "utils/qtcassert.h" #include "utils/runextensions.h" #include "utils/synchronousprocess.h" +#include "utils/qtcprocess.h" #include #include +#include #include #include @@ -57,6 +59,10 @@ const char commonArgsKey[] = "Common Arguments:"; const int sdkManagerCmdTimeoutS = 60; const int sdkManagerOperationTimeoutS = 600; +const QRegularExpression assertionReg("(\\(\\s*y\\s*[\\/\\\\]\\s*n\\s*\\)\\s*)(?[\\:\\?])", + QRegularExpression::CaseInsensitiveOption | + QRegularExpression::MultilineOption); + using namespace Utils; using SdkCmdFutureInterface = QFutureInterface; @@ -88,7 +94,7 @@ static bool valueForKey(QString key, const QString &line, QString *value = nullp return false; } -int parseProgress(const QString &out) +int parseProgress(const QString &out, bool &foundAssertion) { int progress = -1; if (out.isEmpty()) @@ -102,6 +108,8 @@ int parseProgress(const QString &out) if (progress < 0 || progress > 100) progress = -1; } + if (!foundAssertion) + foundAssertion = assertionReg.match(line).hasMatch(); } return progress; } @@ -146,12 +154,15 @@ static void sdkManagerCommand(const Utils::FileName &toolPath, const QStringList { int offset = fi.progressValue(); SynchronousProcess proc; + bool assertionFound = false; proc.setStdErrBufferedSignalsEnabled(true); proc.setStdOutBufferedSignalsEnabled(true); proc.setTimeoutS(timeout); QObject::connect(&proc, &SynchronousProcess::stdOutBuffered, - [offset, progressQuota, &fi](const QString &out) { - int progressPercent = parseProgress(out); + [offset, progressQuota, &proc, &assertionFound, &fi](const QString &out) { + int progressPercent = parseProgress(out, assertionFound); + if (assertionFound) + proc.terminate(); if (progressPercent != -1) fi.setProgressValue(offset + qRound((progressPercent / 100.0) * progressQuota)); }); @@ -163,7 +174,15 @@ static void sdkManagerCommand(const Utils::FileName &toolPath, const QStringList &proc, &SynchronousProcess::terminate); } SynchronousProcessResponse response = proc.run(toolPath.toString(), args); - output.success = response.result == SynchronousProcessResponse::Finished; + if (assertionFound) { + output.success = false; + output.stdOutput = response.stdOut(); + output.stdError = QCoreApplication::translate("Android::Internal::AndroidSdkManager", + "Operation requires user interaction." + "Please use \"sdkmanager\" commandline tool"); + } else { + output.success = response.result == SynchronousProcessResponse::Finished; + } } @@ -183,19 +202,29 @@ public: void updateInstalled(SdkCmdFutureInterface &fi); void update(SdkCmdFutureInterface &fi, const QStringList &install, const QStringList &uninstall); + void checkPendingLicense(SdkCmdFutureInterface &fi); + void getPendingLicense(SdkCmdFutureInterface &fi); void addWatcher(const QFuture &future); + void setLicenseInput(bool acceptLicense); std::unique_ptr, decltype(&watcherDeleter)> m_activeOperation; private: + QByteArray getUserInput() const; + void clearUserInput(); void reloadSdkPackages(); void clearPackages(); + bool onLicenseStdOut(const QString &output, bool notify, + AndroidSdkManager::OperationOutput &result, SdkCmdFutureInterface &fi); AndroidSdkManager &m_sdkManager; const AndroidConfig &m_config; AndroidSdkPackageList m_allPackages; FileName lastSdkManagerPath; + QString m_licenseTextCache; + QByteArray m_licenseUserInput; + mutable QReadWriteLock m_licenseInputLock; }; /*! @@ -362,12 +391,35 @@ AndroidSdkManager::update(const QStringList &install, const QStringList &uninsta return future; } +QFuture AndroidSdkManager::checkPendingLicenses() +{ + if (isBusy()) + return QFuture(); + auto future = Utils::runAsync(&AndroidSdkManagerPrivate::checkPendingLicense, m_d.get()); + m_d->addWatcher(future); + return future; +} + +QFuture AndroidSdkManager::runLicenseCommand() +{ + if (isBusy()) + return QFuture(); + auto future = Utils::runAsync(&AndroidSdkManagerPrivate::getPendingLicense, m_d.get()); + m_d->addWatcher(future); + return future; +} + void AndroidSdkManager::cancelOperatons() { emit cancelActiveOperations(); m_d->m_activeOperation.reset(); } +void AndroidSdkManager::acceptSdkLicense(bool accept) +{ + m_d->setLicenseInput(accept); +} + void SdkManagerOutputParser::parsePackageListing(const QString &output) { QStringList packageData; @@ -809,6 +861,120 @@ void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringLi fi.setProgressValue(100); } +void AndroidSdkManagerPrivate::checkPendingLicense(SdkCmdFutureInterface &fi) +{ + fi.setProgressRange(0, 100); + fi.setProgressValue(0); + AndroidSdkManager::OperationOutput result; + result.type = AndroidSdkManager::LicenseCheck; + QStringList args("--licenses"); + if (!fi.isCanceled()) + sdkManagerCommand(m_config.sdkManagerToolPath(), args, m_sdkManager, fi, result, 100.0); + else + qCDebug(sdkManagerLog) << "Update: Operation cancelled before start"; + + fi.reportResult(result); + fi.setProgressValue(100); +} + +void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi) +{ + fi.setProgressRange(0, 100); + fi.setProgressValue(0); + AndroidSdkManager::OperationOutput result; + result.type = AndroidSdkManager::LicenseWorkflow; + QtcProcess licenseCommand; + bool reviewingLicenses = false; + licenseCommand.setCommand(m_config.sdkManagerToolPath().toString(), {"--licenses"}); + if (Utils::HostOsInfo::isWindowsHost()) + licenseCommand.setUseCtrlCStub(true); + licenseCommand.start(); + QTextCodec *codec = QTextCodec::codecForLocale(); + int inputCounter = 0, steps = -1; + while (!licenseCommand.waitForFinished(200)) { + QString stdOut = codec->toUnicode(licenseCommand.readAllStandardOutput()); + bool assertionFound = false; + if (!stdOut.isEmpty()) + assertionFound = onLicenseStdOut(stdOut, reviewingLicenses, result, fi); + + if (reviewingLicenses) { + // Check user input + QByteArray userInput = getUserInput(); + if (!userInput.isEmpty()) { + clearUserInput(); + licenseCommand.write(userInput); + ++inputCounter; + if (steps != -1) + fi.setProgressValue(qRound((inputCounter / (double)steps) * 100)); + } + } else if (assertionFound) { + // The first assertion is to start reviewing licenses. Always accept. + reviewingLicenses = true; + QRegularExpression reg("(\\d+\\sof\\s)(?\\d+)"); + QRegularExpressionMatch match = reg.match(stdOut); + if (match.hasMatch()) + steps = match.captured("steps").toInt(); + licenseCommand.write("Y\n"); + } + + if (fi.isCanceled()) { + licenseCommand.terminate(); + if (!licenseCommand.waitForFinished(300)) { + licenseCommand.kill(); + licenseCommand.waitForFinished(200); + } + } + if (licenseCommand.state() == QProcess::NotRunning) + break; + } + + m_licenseTextCache.clear(); + result.success = licenseCommand.exitStatus() == QtcProcess::NormalExit; + if (!result.success) { + result.stdError = QCoreApplication::translate("Android::Internal::AndroidSdkManager", + "License command failed.\n\n"); + } + fi.reportResult(result); + fi.setProgressValue(100); +} + +void AndroidSdkManagerPrivate::setLicenseInput(bool acceptLicense) +{ + QWriteLocker locker(&m_licenseInputLock); + m_licenseUserInput = acceptLicense ? "Y\n" : "n\n"; +} + +QByteArray AndroidSdkManagerPrivate::getUserInput() const +{ + QReadLocker locker(&m_licenseInputLock); + return m_licenseUserInput; +} + +void AndroidSdkManagerPrivate::clearUserInput() +{ + QWriteLocker locker(&m_licenseInputLock); + m_licenseUserInput.clear(); +} + +bool AndroidSdkManagerPrivate::onLicenseStdOut(const QString &output, bool notify, + AndroidSdkManager::OperationOutput &result, + SdkCmdFutureInterface &fi) +{ + m_licenseTextCache.append(output); + QRegularExpressionMatch assertionMatch = assertionReg.match(m_licenseTextCache); + if (assertionMatch.hasMatch()) { + if (notify) { + result.stdOutput = m_licenseTextCache; + fi.reportResult(result); + } + // Clear the current contents. The found license text is dispatched. Continue collecting the + // next license text. + m_licenseTextCache.clear(); + return true; + } + return false; +} + void AndroidSdkManagerPrivate::addWatcher(const QFuture &future) { if (future.isFinished()) diff --git a/src/plugins/android/androidsdkmanager.h b/src/plugins/android/androidsdkmanager.h index db2316aee82..fa15002cd2d 100644 --- a/src/plugins/android/androidsdkmanager.h +++ b/src/plugins/android/androidsdkmanager.h @@ -48,7 +48,9 @@ public: { None, UpdateAll, - UpdatePackage + UpdatePackage, + LicenseCheck, + LicenseWorkflow }; struct OperationOutput @@ -78,8 +80,11 @@ public: QFuture availableArguments() const; QFuture updateAll(); QFuture update(const QStringList &install, const QStringList &uninstall); + QFuture checkPendingLicenses(); + QFuture runLicenseCommand(); void cancelOperatons(); + void acceptSdkLicense(bool accept); signals: void packageReloadBegin(); diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp index 35a3685d578..7ffa294e318 100644 --- a/src/plugins/android/androidsdkmanagerwidget.cpp +++ b/src/plugins/android/androidsdkmanagerwidget.cpp @@ -89,6 +89,8 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config, { QTC_CHECK(sdkManager); m_ui->setupUi(this); + m_ui->sdkLicensebuttonBox->hide(); + m_ui->sdkLicenseLabel->hide(); m_ui->warningLabel->setElideMode(Qt::ElideRight); m_ui->warningIconLabel->setPixmap(Utils::Icons::WARNING.pixmap()); m_ui->viewStack->setCurrentWidget(m_ui->packagesStack); @@ -151,6 +153,14 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config, this, &AndroidSdkManagerWidget::onNativeSdkManager); connect(m_ui->optionsButton, &QPushButton::clicked, this, &AndroidSdkManagerWidget::onSdkManagerOptions); + connect(m_ui->sdkLicensebuttonBox, &QDialogButtonBox::accepted, [this]() { + m_sdkManager->acceptSdkLicense(true); + m_ui->sdkLicensebuttonBox->setEnabled(false); // Wait for next license to enable controls + }); + connect(m_ui->sdkLicensebuttonBox, &QDialogButtonBox::rejected, [this]() { + m_sdkManager->acceptSdkLicense(false); + m_ui->sdkLicensebuttonBox->setEnabled(false); // Wait for next license to enable controls + }); } AndroidSdkManagerWidget::~AndroidSdkManagerWidget() @@ -179,9 +189,15 @@ void AndroidSdkManagerWidget::installEssentials() m_ui->applySelectionButton->click(); } +void AndroidSdkManagerWidget::beginLicenseCheck() +{ + m_formatter->appendMessage(tr("Checking pending licenses...\n"), Utils::NormalMessageFormat); + addPackageFuture(m_sdkManager->checkPendingLicenses()); +} + void AndroidSdkManagerWidget::onApplyButton() { - QTC_ASSERT(currentView() == PackageListing, return); + QTC_ASSERT(m_currentView == PackageListing, return); if (m_sdkManager->isBusy()) { m_formatter->appendMessage(tr("\nSDK Manager is busy."), Utils::StdErrFormat); @@ -192,16 +208,13 @@ void AndroidSdkManagerWidget::onApplyButton() if (packagesToUpdate.isEmpty()) return; - QStringList installPackages, uninstallPackages, installSdkPaths, uninstallSdkPaths; + QStringList installPackages, uninstallPackages; for (auto package : packagesToUpdate) { QString str = QString(" %1").arg(package->descriptionText()); - if (package->state() == AndroidSdkPackage::Installed) { - uninstallSdkPaths << package->sdkStylePath(); + if (package->state() == AndroidSdkPackage::Installed) uninstallPackages << str; - } else { - installSdkPaths << package->sdkStylePath(); + else installPackages << str; - } } QMessageBox messageDlg(QMessageBox::Information, tr("Android SDK Changes"), @@ -222,17 +235,16 @@ void AndroidSdkManagerWidget::onApplyButton() if (messageDlg.exec() == QMessageBox::Cancel) return; - // User agreed with the selection. Begin packages install/uninstall - emit updatingSdk(); switchView(Operations); - m_formatter->appendMessage(tr("Updating selected packages...\n"), - Utils::NormalMessageFormat); - m_formatter->appendMessage(tr("Closing the %1 dialog will cancel the running and scheduled SDK " - "operations.\n").arg(Utils::HostOsInfo::isMacHost() ? - tr("preferences") : tr("options")), - Utils::LogMessageFormat); - - addPackageFuture(m_sdkManager->update(installSdkPaths, uninstallSdkPaths)); + m_pendingCommand = AndroidSdkManager::UpdatePackage; + // User agreed with the selection. Check for licenses. + if (!installPackages.isEmpty()) { + // Pending license affects installtion only. + beginLicenseCheck(); + } else { + // Uninstall only. Go Ahead. + beginExecution(); + } } void AndroidSdkManagerWidget::onUpdatePackages() @@ -242,8 +254,8 @@ void AndroidSdkManagerWidget::onUpdatePackages() return; } switchView(Operations); - m_formatter->appendMessage(tr("Updating installed packages\n"), Utils::NormalMessageFormat); - addPackageFuture(m_sdkManager->updateAll()); + m_pendingCommand = AndroidSdkManager::UpdateAll; + beginLicenseCheck(); } void AndroidSdkManagerWidget::onCancel() @@ -267,12 +279,42 @@ void AndroidSdkManagerWidget::onNativeSdkManager() void AndroidSdkManagerWidget::onOperationResult(int index) { QTC_ASSERT(m_currentOperation, return); - auto breakLine = [](const QString &line) { return line.endsWith("\n") ? line : line + "\n";}; AndroidSdkManager::OperationOutput result = m_currentOperation->resultAt(index); - if (!result.stdError.isEmpty()) + if (result.type == AndroidSdkManager::LicenseWorkflow) { + // Show license controls and enable to user input. + m_ui->sdkLicenseLabel->setVisible(true); + m_ui->sdkLicensebuttonBox->setVisible(true); + m_ui->sdkLicensebuttonBox->setEnabled(true); + m_ui->sdkLicensebuttonBox->button(QDialogButtonBox::No)->setDefault(true); + } + auto breakLine = [](const QString &line) { return line.endsWith("\n") ? line : line + "\n";}; + if (!result.stdError.isEmpty() && result.type != AndroidSdkManager::LicenseCheck) m_formatter->appendMessage(breakLine(result.stdError), Utils::StdErrFormat); - if (!result.stdOutput.isEmpty()) + if (!result.stdOutput.isEmpty() && result.type != AndroidSdkManager::LicenseCheck) m_formatter->appendMessage(breakLine(result.stdOutput), Utils::StdOutFormat); + m_ui->outputEdit->ensureCursorVisible(); +} + +void AndroidSdkManagerWidget::onLicenseCheckResult(const AndroidSdkManager::OperationOutput& output) +{ + if (output.success) { + // No assertion was found. Looks like all license are accepted. Go Ahead. + runPendingCommand(); + } else { + // Assertion was found. Provide user workflow to accept licenses. + QString warningMessage = tr("\nPlease note that the installation and use of Android SDK " + "packages may fail if respective licenses are not accepted."); + int userSelection = QMessageBox::question(this, tr("Android SDK Licenses"), + output.stdOutput + warningMessage, + QMessageBox::Yes | QMessageBox::No); + if (userSelection == QMessageBox::Yes) { + // Run license workflow. + beginLicenseWorkflow(); + } else { + // User decided to go ahead anyways. + runPendingCommand(); + } + } } void AndroidSdkManagerWidget::addPackageFuture(const QFuture @@ -299,33 +341,100 @@ void AndroidSdkManagerWidget::addPackageFuture(const QFuture packagesToUpdate = m_sdkModel->userSelection(); + if (packagesToUpdate.isEmpty()) { + switchView(PackageListing); + return; + } + + QStringList installSdkPaths, uninstallSdkPaths; + for (auto package : packagesToUpdate) { + if (package->state() == AndroidSdkPackage::Installed) + uninstallSdkPaths << package->sdkStylePath(); + else + installSdkPaths << package->sdkStylePath(); + } + m_formatter->appendMessage(tr("Installing/Uninstalling selected packages...\n"), + Utils::NormalMessageFormat); + m_formatter->appendMessage(tr("Closing the %1 dialog will cancel the running and scheduled SDK " + "operations.\n").arg(Utils::HostOsInfo::isMacHost() ? + tr("preferences") : tr("options")), + Utils::LogMessageFormat); + + addPackageFuture(m_sdkManager->update(installSdkPaths, uninstallSdkPaths)); +} + +void AndroidSdkManagerWidget::beginUpdate() +{ + m_formatter->appendMessage(tr("Updating installed packages...\n"), Utils::NormalMessageFormat); + m_formatter->appendMessage(tr("Closing the %1 dialog will cancel the running and scheduled SDK " + "operations.\n").arg(Utils::HostOsInfo::isMacHost() ? + tr("preferences") : tr("options")), + Utils::LogMessageFormat); + addPackageFuture(m_sdkManager->updateAll()); +} + +void AndroidSdkManagerWidget::beginLicenseWorkflow() +{ + switchView(LicenseWorkflow); + addPackageFuture(m_sdkManager->runLicenseCommand()); +} + void AndroidSdkManagerWidget::notifyOperationFinished() { if (!m_currentOperation || m_currentOperation->isFinished()) { QMessageBox::information(this, tr("Android SDK Changes"), tr("Android SDK operations finished."), QMessageBox::Ok); - switchView(PackageListing); m_ui->operationProgress->setValue(0); - m_sdkManager->reloadPackages(true); - emit updatingSdkFinished(); } } void AndroidSdkManagerWidget::packageFutureFinished() { - if (!m_currentOperation) { - qCDebug(androidSdkMgrUiLog) << "Invalid State. No active operation."; - return; - } else if (m_currentOperation->isCanceled()) { + QTC_ASSERT (m_currentOperation, return); + + bool continueWorkflow = true; + if (m_currentOperation->isCanceled()) { m_formatter->appendMessage(tr("Operation cancelled.\n"), Utils::StdErrFormat); + continueWorkflow = false; } m_ui->operationProgress->setValue(100); - m_currentOperation->deleteLater(); - m_currentOperation = nullptr; - notifyOperationFinished(); + int resultCount = m_currentOperation->future().resultCount(); + if (continueWorkflow && resultCount > 0) { + AndroidSdkManager::OperationOutput output = m_currentOperation->resultAt(resultCount -1); + AndroidSdkManager::CommandType type = output.type; + m_currentOperation->deleteLater(); + m_currentOperation = nullptr; + switch (type) { + case AndroidSdkManager::LicenseCheck: + onLicenseCheckResult(output); + break; + case AndroidSdkManager::LicenseWorkflow: + m_ui->sdkLicensebuttonBox->hide(); + m_ui->sdkLicenseLabel->hide(); + runPendingCommand(); + break; + case AndroidSdkManager::UpdateAll: + case AndroidSdkManager::UpdatePackage: + notifyOperationFinished(); + switchView(PackageListing); + m_sdkManager->reloadPackages(true); + break; + default: + break; + } + } else { + m_currentOperation->deleteLater(); + m_currentOperation = nullptr; + switchView(PackageListing); + m_sdkManager->reloadPackages(true); + } } void AndroidSdkManagerWidget::cancelPendingOperations() @@ -333,6 +442,7 @@ void AndroidSdkManagerWidget::cancelPendingOperations() if (!m_sdkManager->isBusy()) { m_formatter->appendMessage(tr("\nNo pending operations to cancel...\n"), Utils::NormalMessageFormat); + switchView(PackageListing); return; } m_formatter->appendMessage(tr("\nCancelling pending operations...\n"), @@ -342,15 +452,29 @@ void AndroidSdkManagerWidget::cancelPendingOperations() void AndroidSdkManagerWidget::switchView(AndroidSdkManagerWidget::View view) { - m_ui->viewStack->setCurrentWidget(view == PackageListing ? + if (m_currentView == PackageListing) { + m_formatter->clear(); + m_ui->outputEdit->clear(); + } + m_currentView = view; + if (m_currentView == PackageListing) + emit updatingSdkFinished(); + else + emit updatingSdk(); + + m_ui->operationProgress->setValue(0); + m_ui->viewStack->setCurrentWidget(m_currentView == PackageListing ? m_ui->packagesStack : m_ui->outputStack); - m_formatter->clear(); - m_ui->outputEdit->clear(); } -AndroidSdkManagerWidget::View AndroidSdkManagerWidget::currentView() const +void AndroidSdkManagerWidget::runPendingCommand() { - return m_ui->viewStack->currentWidget() == m_ui->packagesStack ? PackageListing : Operations; + if (m_pendingCommand == AndroidSdkManager::UpdatePackage) + beginExecution(); // License workflow can only start when updating packages. + else if (m_pendingCommand == AndroidSdkManager::UpdateAll) + beginUpdate(); + else + QTC_ASSERT(false, qCDebug(androidSdkMgrUiLog) << "Unexpected state: No pending command."); } void AndroidSdkManagerWidget::onSdkManagerOptions() diff --git a/src/plugins/android/androidsdkmanagerwidget.h b/src/plugins/android/androidsdkmanagerwidget.h index c111fa0b968..29b2ec064e1 100644 --- a/src/plugins/android/androidsdkmanagerwidget.h +++ b/src/plugins/android/androidsdkmanagerwidget.h @@ -48,7 +48,8 @@ class AndroidSdkManagerWidget : public QWidget enum View { PackageListing, - Operations + Operations, + LicenseWorkflow }; public: @@ -69,15 +70,22 @@ private: void onCancel(); void onNativeSdkManager(); void onOperationResult(int index); + void onLicenseCheckResult(const AndroidSdkManager::OperationOutput &output); void onSdkManagerOptions(); void addPackageFuture(const QFuture &future); + void beginLicenseCheck(); + void beginExecution(); + void beginUpdate(); + void beginLicenseWorkflow(); void notifyOperationFinished(); void packageFutureFinished(); void cancelPendingOperations(); void switchView(View view); - View currentView() const; + void runPendingCommand(); AndroidConfig &m_androidConfig; + AndroidSdkManager::CommandType m_pendingCommand = AndroidSdkManager::None; + View m_currentView = PackageListing; AndroidSdkManager *m_sdkManager = nullptr; AndroidSdkModel *m_sdkModel = nullptr; Ui::AndroidSdkManagerWidget *m_ui = nullptr; diff --git a/src/plugins/android/androidsdkmanagerwidget.ui b/src/plugins/android/androidsdkmanagerwidget.ui index ac5b8526663..335ed19a5d9 100644 --- a/src/plugins/android/androidsdkmanagerwidget.ui +++ b/src/plugins/android/androidsdkmanagerwidget.ui @@ -211,34 +211,14 @@ - + Cancel - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - true - - - - + 100 @@ -254,6 +234,58 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + + + + + + 0 + 0 + + + + Do you want to accept the Android SDK license? + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 0 + 0 + + + + QDialogButtonBox::No|QDialogButtonBox::Yes + + + From f93a55ac5c41cb0407a3ece1602a83ca23065b74 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 6 Oct 2017 16:49:47 +0200 Subject: [PATCH 25/27] QbsProjectManager: Fix build setting getting lost If the user set some qbs property and also unchecked the "Use default installation" box, the latter setting got lost on re-loading the build step configuration. Task-number: QTCREATORBUG-18895 Change-Id: I868312fbbd8e5ca9ad9f34b88866fe7b72884a9a Reviewed-by: Jake Petroules --- src/plugins/qbsprojectmanager/qbsbuildstep.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp index 063e6bf203b..a212ae8567f 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp @@ -780,6 +780,9 @@ void QbsBuildStepConfigWidget::applyCachedProperties() if (tmp.contains(Constants::QBS_CONFIG_QUICK_DEBUG_KEY)) data.insert(Constants::QBS_CONFIG_QUICK_DEBUG_KEY, tmp.value(Constants::QBS_CONFIG_QUICK_DEBUG_KEY)); + if (tmp.contains(Constants::QBS_INSTALL_ROOT_KEY)) + data.insert(Constants::QBS_INSTALL_ROOT_KEY, + tmp.value(Constants::QBS_INSTALL_ROOT_KEY)); for (int i = 0; i < m_propertyCache.count(); ++i) { const Property &property = m_propertyCache.at(i); From 01f81fa4dca0c9e2fdfed7da3e71a505bf940bf1 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 6 Oct 2017 17:06:59 +0200 Subject: [PATCH 26/27] QbsProjectManager: Get rid of redundant code Change-Id: I44c5058161312c832bf1e74c417007d02f2f4f7b Reviewed-by: Jake Petroules --- src/plugins/qbsprojectmanager/qbsbuildstep.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp index a212ae8567f..6dc69eb6582 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp @@ -774,15 +774,14 @@ void QbsBuildStepConfigWidget::applyCachedProperties() tmp.value(Constants::QBS_CONFIG_PROFILE_KEY)); data.insert(Constants::QBS_CONFIG_VARIANT_KEY, tmp.value(Constants::QBS_CONFIG_VARIANT_KEY)); - if (tmp.contains(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY)) - data.insert(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY, - tmp.value(Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY)); - if (tmp.contains(Constants::QBS_CONFIG_QUICK_DEBUG_KEY)) - data.insert(Constants::QBS_CONFIG_QUICK_DEBUG_KEY, - tmp.value(Constants::QBS_CONFIG_QUICK_DEBUG_KEY)); - if (tmp.contains(Constants::QBS_INSTALL_ROOT_KEY)) - data.insert(Constants::QBS_INSTALL_ROOT_KEY, - tmp.value(Constants::QBS_INSTALL_ROOT_KEY)); + const QStringList additionalSpecialKeys({Constants::QBS_CONFIG_DECLARATIVE_DEBUG_KEY, + Constants::QBS_CONFIG_QUICK_DEBUG_KEY, + Constants::QBS_INSTALL_ROOT_KEY}); + for (const QString &key : additionalSpecialKeys) { + const auto it = tmp.constFind(key); + if (it != tmp.cend()) + data.insert(key, it.value()); + } for (int i = 0; i < m_propertyCache.count(); ++i) { const Property &property = m_propertyCache.at(i); From cd575e3a7644f817f2a98b65350b379774f1e23f Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 11 Oct 2017 16:43:53 +0200 Subject: [PATCH 27/27] Do not arbitrarily create directories The JsonSchemaManager force-created a "json" directory, even in the installation resource path. This is unnecessary and ugly, because it creates that even within the Qt Creator installation from e.g. the online installer, which results in directories being left over when uninstalling it. Change-Id: I5c683353b124c219c5f065209212a84a4c7ccd0f Reviewed-by: Marco Benelli Reviewed-by: Christian Stenger --- src/libs/utils/json.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/utils/json.cpp b/src/libs/utils/json.cpp index d0cc0bbd136..ae97e1fd528 100644 --- a/src/libs/utils/json.cpp +++ b/src/libs/utils/json.cpp @@ -671,7 +671,7 @@ JsonSchemaManager::JsonSchemaManager(const QStringList &searchPaths) { foreach (const QString &path, m_searchPaths) { QDir dir(path); - if (!dir.exists() && !dir.mkpath(path)) + if (!dir.exists()) continue; dir.setNameFilters(QStringList(QLatin1String("*.json"))); foreach (const QFileInfo &fi, dir.entryInfoList())