From c8393b10fd7be6fa347d2f161a3a0cc9b14c15fb Mon Sep 17 00:00:00 2001
From: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
Date: Mon, 30 Sep 2013 15:32:06 +0200
Subject: [PATCH] Extra library editor for Android

Add a list view to the deployment settings which allows you
to add and remove libraries from the ANDROID_EXTRA_LIBS
variable in the .pro file.

Task-number: QTCREATORBUG-9849
Change-Id: Ic0131c46be8fdef4b226b5ceb0ee82ea4dd82c6a
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
---
 src/plugins/android/android.pro               |   6 +-
 src/plugins/android/androiddeployqtwidget.cpp |  45 +++++
 src/plugins/android/androiddeployqtwidget.h   |   7 +
 src/plugins/android/androiddeployqtwidget.ui  | 191 ++++++++++++------
 .../android/androidextralibrarylistmodel.cpp  | 131 ++++++++++++
 .../android/androidextralibrarylistmodel.h    |  67 ++++++
 src/plugins/qt4projectmanager/qt4nodes.cpp    |   1 +
 src/plugins/qt4projectmanager/qt4nodes.h      |   3 +-
 8 files changed, 386 insertions(+), 65 deletions(-)
 create mode 100644 src/plugins/android/androidextralibrarylistmodel.cpp
 create mode 100644 src/plugins/android/androidextralibrarylistmodel.h

diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro
index dbb32f2515f..31a3a81a359 100644
--- a/src/plugins/android/android.pro
+++ b/src/plugins/android/android.pro
@@ -47,7 +47,8 @@ HEADERS += \
     certificatesmodel.h \
     androiddeployqtwidget.h \
     createandroidmanifestwizard.h \
-    androidpotentialkit.h
+    androidpotentialkit.h \
+    androidextralibrarylistmodel.h
 
 SOURCES += \
     androidconfigurations.cpp \
@@ -89,7 +90,8 @@ SOURCES += \
     certificatesmodel.cpp \
     androiddeployqtwidget.cpp \
     createandroidmanifestwizard.cpp \
-    androidpotentialkit.cpp
+    androidpotentialkit.cpp \
+    androidextralibrarylistmodel.cpp
 
 FORMS += \
     androidsettingswidget.ui \
diff --git a/src/plugins/android/androiddeployqtwidget.cpp b/src/plugins/android/androiddeployqtwidget.cpp
index c184890b9a5..8a46ebce5ea 100644
--- a/src/plugins/android/androiddeployqtwidget.cpp
+++ b/src/plugins/android/androiddeployqtwidget.cpp
@@ -35,6 +35,7 @@
 #include "androiddeployqtstep.h"
 #include "androidmanager.h"
 #include "createandroidmanifestwizard.h"
+#include "androidextralibrarylistmodel.h"
 
 #include <projectexplorer/target.h>
 #include <qt4projectmanager/qt4buildconfiguration.h>
@@ -133,6 +134,19 @@ AndroidDeployQtWidget::AndroidDeployQtWidget(AndroidDeployQtStep *step)
 
     connect(m_ui->createAndroidManifestButton, SIGNAL(clicked()),
             this, SLOT(createManifestButton()));
+
+    m_extraLibraryListModel = new AndroidExtraLibraryListModel(static_cast<Qt4ProjectManager::Qt4Project *>(m_step->project()),
+                                                               this);
+    m_ui->androidExtraLibsListView->setModel(m_extraLibraryListModel);
+
+    connect(m_ui->androidExtraLibsListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+            this, SLOT(checkEnableRemoveButton()));
+
+    connect(m_ui->addAndroidExtraLibButton, SIGNAL(clicked()), this, SLOT(addAndroidExtraLib()));
+    connect(m_ui->removeAndroidExtraLibButton, SIGNAL(clicked()), this, SLOT(removeAndroidExtraLib()));
+
+    connect(m_step->project(), SIGNAL(proFilesEvaluated()), this, SLOT(checkProjectTemplate()));
+    checkProjectTemplate();
 }
 
 AndroidDeployQtWidget::~AndroidDeployQtWidget()
@@ -140,6 +154,15 @@ AndroidDeployQtWidget::~AndroidDeployQtWidget()
     delete m_ui;
 }
 
+void AndroidDeployQtWidget::checkProjectTemplate()
+{
+    Qt4ProjectManager::Qt4Project *project = static_cast<Qt4ProjectManager::Qt4Project *>(m_step->project());
+    if (project->rootQt4ProjectNode()->projectType() == Qt4ProjectManager::ApplicationTemplate)
+        m_ui->additionalLibrariesGroupBox->setEnabled(true);
+    else
+        m_ui->additionalLibrariesGroupBox->setEnabled(false);
+}
+
 void AndroidDeployQtWidget::createManifestButton()
 {
     CreateAndroidManifestWizard wizard(m_step->target());
@@ -322,3 +345,25 @@ void AndroidDeployQtWidget::updateSigningWarning()
         m_ui->signingDebugWarningLabel->setVisible(false);
     }
 }
+
+void AndroidDeployQtWidget::addAndroidExtraLib()
+{
+    QStringList fileNames = QFileDialog::getOpenFileNames(this,
+                                                          tr("Select additional libraries"),
+                                                          QDir::homePath(),
+                                                          tr("Libraries (*.so)"));
+
+    if (!fileNames.isEmpty())
+        m_extraLibraryListModel->addEntries(fileNames);
+}
+
+void AndroidDeployQtWidget::removeAndroidExtraLib()
+{
+    QModelIndexList removeList = m_ui->androidExtraLibsListView->selectionModel()->selectedIndexes();
+    m_extraLibraryListModel->removeEntries(removeList);
+}
+
+void AndroidDeployQtWidget::checkEnableRemoveButton()
+{
+    m_ui->removeAndroidExtraLibButton->setEnabled(m_ui->androidExtraLibsListView->selectionModel()->hasSelection());
+}
diff --git a/src/plugins/android/androiddeployqtwidget.h b/src/plugins/android/androiddeployqtwidget.h
index 849e30f35d8..a3ef1d9b960 100644
--- a/src/plugins/android/androiddeployqtwidget.h
+++ b/src/plugins/android/androiddeployqtwidget.h
@@ -43,6 +43,7 @@ namespace Qt4ProjectManager { class Qt4BuildConfiguration; }
 namespace Android {
 namespace Internal {
 class AndroidDeployQtStep;
+class AndroidExtraLibraryListModel;
 class AndroidDeployQtWidget : public ProjectExplorer::BuildStepConfigWidget
 {
     Q_OBJECT
@@ -71,6 +72,11 @@ private slots:
     void updateInputFileUi();
     void inputFileComboBoxIndexChanged();
     void createManifestButton();
+    void addAndroidExtraLib();
+    void removeAndroidExtraLib();
+    void checkEnableRemoveButton();
+    void checkProjectTemplate();
+
 private:
     virtual QString summaryText() const;
     virtual QString displayName() const;
@@ -78,6 +84,7 @@ private:
 
     Ui::AndroidDeployQtWidget *m_ui;
     AndroidDeployQtStep *m_step;
+    AndroidExtraLibraryListModel *m_extraLibraryListModel;
     Qt4ProjectManager::Qt4BuildConfiguration *m_currentBuildConfiguration;
     bool m_ignoreChange;
 };
diff --git a/src/plugins/android/androiddeployqtwidget.ui b/src/plugins/android/androiddeployqtwidget.ui
index 6d60e646c79..c432b4a5464 100644
--- a/src/plugins/android/androiddeployqtwidget.ui
+++ b/src/plugins/android/androiddeployqtwidget.ui
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>682</width>
-    <height>467</height>
+    <height>615</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -80,7 +80,7 @@
            <string/>
           </property>
           <property name="pixmap">
-           <pixmap resource="../projectexplorer/projectexplorer.qrc">:/projectexplorer/images/compile_warning.png</pixmap>
+           <pixmap>:/projectexplorer/images/compile_warning.png</pixmap>
           </property>
          </widget>
         </item>
@@ -137,62 +137,6 @@
      </layout>
     </widget>
    </item>
-   <item row="3" column="0">
-    <widget class="QGroupBox" name="qtDeployment">
-     <property name="title">
-      <string>Qt Deployment</string>
-     </property>
-     <layout class="QVBoxLayout" name="verticalLayout">
-      <item>
-       <widget class="QRadioButton" name="ministroOption">
-        <property name="toolTip">
-         <string>Use the external Ministro application to download and maintain Qt libraries.</string>
-        </property>
-        <property name="text">
-         <string>Use Ministro service to install Qt</string>
-        </property>
-        <property name="checked">
-         <bool>true</bool>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <widget class="QRadioButton" name="temporaryQtOption">
-        <property name="toolTip">
-         <string>Push local Qt libraries to device. You must have Qt libraries compiled for that platform.
-The APK will not be usable on any other device.</string>
-        </property>
-        <property name="text">
-         <string>Deploy local Qt libraries to temporary directory</string>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <widget class="QRadioButton" name="bundleQtOption">
-        <property name="toolTip">
-         <string>Creates a standalone APK.</string>
-        </property>
-        <property name="text">
-         <string>Bundle Qt libraries in APK</string>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <spacer name="verticalSpacer">
-        <property name="orientation">
-         <enum>Qt::Vertical</enum>
-        </property>
-        <property name="sizeHint" stdset="0">
-         <size>
-          <width>20</width>
-          <height>40</height>
-         </size>
-        </property>
-       </spacer>
-      </item>
-     </layout>
-    </widget>
-   </item>
    <item row="3" column="1">
     <widget class="QGroupBox" name="advancedActions">
      <property name="title">
@@ -287,7 +231,7 @@ The APK will not be usable on any other device.</string>
         <string/>
        </property>
        <property name="pixmap">
-        <pixmap resource="../projectexplorer/projectexplorer.qrc">:/projectexplorer/images/compile_warning.png</pixmap>
+        <pixmap>:/projectexplorer/images/compile_warning.png</pixmap>
        </property>
        <property name="alignment">
         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
@@ -306,10 +250,133 @@ The APK will not be usable on any other device.</string>
      </item>
     </layout>
    </item>
+   <item row="3" column="0">
+    <widget class="QGroupBox" name="qtDeployment">
+     <property name="title">
+      <string>Qt Deployment</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QRadioButton" name="ministroOption">
+        <property name="toolTip">
+         <string>Use the external Ministro application to download and maintain Qt libraries.</string>
+        </property>
+        <property name="text">
+         <string>Use Ministro service to install Qt</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="temporaryQtOption">
+        <property name="toolTip">
+         <string>Push local Qt libraries to device. You must have Qt libraries compiled for that platform.
+The APK will not be usable on any other device.</string>
+        </property>
+        <property name="text">
+         <string>Deploy local Qt libraries to temporary directory</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="bundleQtOption">
+        <property name="toolTip">
+         <string>Creates a standalone APK.</string>
+        </property>
+        <property name="text">
+         <string>Bundle Qt libraries in APK</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="verticalSpacer">
+        <property name="orientation">
+         <enum>Qt::Vertical</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>20</width>
+          <height>40</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="4" column="0" colspan="2">
+    <widget class="QGroupBox" name="additionalLibrariesGroupBox">
+     <property name="title">
+      <string>Additional libraries</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <item>
+       <layout class="QHBoxLayout" name="androidExtraLibsLayout">
+        <item>
+         <widget class="QListView" name="androidExtraLibsListView">
+          <property name="toolTip">
+           <string>List of extra libraries to include in Android package and load on start-up.</string>
+          </property>
+          <property name="selectionMode">
+           <enum>QAbstractItemView::ExtendedSelection</enum>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <layout class="QVBoxLayout" name="androidExtraLibsButtonLayout">
+          <item>
+           <widget class="QToolButton" name="addAndroidExtraLibButton">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="toolTip">
+             <string>Select a library to include in package</string>
+            </property>
+            <property name="text">
+             <string>Add</string>
+            </property>
+            <property name="toolButtonStyle">
+             <enum>Qt::ToolButtonTextOnly</enum>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QToolButton" name="removeAndroidExtraLibButton">
+            <property name="toolTip">
+             <string>Remove currently selected library from list</string>
+            </property>
+            <property name="text">
+             <string>Remove</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="verticalSpacer_2">
+            <property name="orientation">
+             <enum>Qt::Vertical</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>20</width>
+              <height>40</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
   </layout>
  </widget>
- <resources>
-  <include location="../projectexplorer/projectexplorer.qrc"/>
- </resources>
+ <resources/>
  <connections/>
 </ui>
diff --git a/src/plugins/android/androidextralibrarylistmodel.cpp b/src/plugins/android/androidextralibrarylistmodel.cpp
new file mode 100644
index 00000000000..3aed0d16512
--- /dev/null
+++ b/src/plugins/android/androidextralibrarylistmodel.cpp
@@ -0,0 +1,131 @@
+/**************************************************************************
+**
+** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com>
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "androidextralibrarylistmodel.h"
+#include <qt4projectmanager/qt4project.h>
+#include <qt4projectmanager/qt4nodes.h>
+
+using namespace Android;
+using namespace Internal;
+
+AndroidExtraLibraryListModel::AndroidExtraLibraryListModel(Qt4ProjectManager::Qt4Project *project,
+                                                           QObject *parent)
+    : QAbstractItemModel(parent)
+    , m_project(project)
+{
+    reset();
+
+    connect(m_project, SIGNAL(proFilesEvaluated()), this, SLOT(reset()));
+}
+
+QModelIndex AndroidExtraLibraryListModel::index(int row, int column, const QModelIndex &) const
+{
+    return createIndex(row, column);
+}
+
+QModelIndex AndroidExtraLibraryListModel::parent(const QModelIndex &) const
+{
+    return QModelIndex();
+}
+
+int AndroidExtraLibraryListModel::rowCount(const QModelIndex &) const
+{
+    return m_entries.size();
+}
+
+int AndroidExtraLibraryListModel::columnCount(const QModelIndex &) const
+{
+    return 1;
+}
+
+QVariant AndroidExtraLibraryListModel::data(const QModelIndex &index, int role) const
+{
+    Q_ASSERT(index.row() >= 0 && index.row() < m_entries.size());
+    const QString &entry = m_entries.at(index.row());
+    switch (role) {
+    case Qt::DisplayRole: return entry;
+    default: return QVariant();
+    };
+}
+
+void AndroidExtraLibraryListModel::reset()
+{
+    if (m_project->rootQt4ProjectNode()->projectType() != Qt4ProjectManager::ApplicationTemplate)
+        return;
+
+    beginResetModel();
+    Qt4ProjectManager::Qt4ProFileNode *node = m_project->rootQt4ProjectNode();
+    m_entries = node->variableValue(Qt4ProjectManager::AndroidExtraLibs);
+    endResetModel();
+}
+
+void AndroidExtraLibraryListModel::addEntries(const QStringList &list)
+{
+    if (m_project->rootQt4ProjectNode()->projectType() != Qt4ProjectManager::ApplicationTemplate)
+        return;
+
+    beginInsertRows(QModelIndex(), m_entries.size(), m_entries.size() + list.size());
+
+    foreach (QString path, list)
+        m_entries += QDir(m_project->projectDirectory()).relativeFilePath(path);
+
+    Qt4ProjectManager::Qt4ProFileNode *node = m_project->rootQt4ProjectNode();
+    node->setProVariable(QLatin1String("ANDROID_EXTRA_LIBS"), m_entries.join(QLatin1Char(' ')));
+
+    endInsertRows();
+}
+
+void AndroidExtraLibraryListModel::removeEntries(const QModelIndexList &list)
+{
+    if (list.isEmpty() || m_project->rootQt4ProjectNode()->projectType() != Qt4ProjectManager::ApplicationTemplate)
+        return;
+
+    QStringList oldList = m_entries;
+    int i = 0;
+    while (i < list.size()) {
+        int firstRow = list.at(i++).row();
+        int lastRow = firstRow;
+        while (i < list.size() && list.at(i).row() - lastRow <= 1 && list.at(i).row() > firstRow)
+            lastRow = list.at(i++).row();
+
+        int first = m_entries.indexOf(oldList.at(firstRow));
+        int count = lastRow - firstRow + 1;
+        Q_ASSERT(count > 0);
+        Q_ASSERT(oldList.at(lastRow) == m_entries.at(first + count - 1));
+
+        beginRemoveRows(QModelIndex(), first, first + count - 1);
+        while (count-- > 0)
+            m_entries.removeAt(first);
+        endRemoveRows();
+    }
+
+    Qt4ProjectManager::Qt4ProFileNode *node = m_project->rootQt4ProjectNode();
+    node->setProVariable(QLatin1String("ANDROID_EXTRA_LIBS"), m_entries.join(QLatin1Char(' ')));
+}
diff --git a/src/plugins/android/androidextralibrarylistmodel.h b/src/plugins/android/androidextralibrarylistmodel.h
new file mode 100644
index 00000000000..657aa30c789
--- /dev/null
+++ b/src/plugins/android/androidextralibrarylistmodel.h
@@ -0,0 +1,67 @@
+/**************************************************************************
+**
+** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com>
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef ANDROIDEXTRALIBRARYLISTMODEL_H
+#define ANDROIDEXTRALIBRARYLISTMODEL_H
+
+#include <QAbstractItemModel>
+
+namespace Qt4ProjectManager { class Qt4Project; }
+
+namespace Android {
+namespace Internal {
+class AndroidExtraLibraryListModel : public QAbstractItemModel
+{
+    Q_OBJECT
+public:
+    explicit AndroidExtraLibraryListModel(Qt4ProjectManager::Qt4Project *project,
+                                          QObject *parent = 0);
+
+    QModelIndex index(int row, int column, const QModelIndex &parent) const;
+    QModelIndex parent(const QModelIndex &child) const;
+    int rowCount(const QModelIndex &parent) const;
+    int columnCount(const QModelIndex &parent) const;
+    QVariant data(const QModelIndex &index, int role) const;
+
+    void removeEntries(const QModelIndexList &list);
+    void addEntries(const QStringList &list);
+
+private slots:
+    void reset();
+
+private:
+    Qt4ProjectManager::Qt4Project *m_project;
+    QStringList m_entries;
+};
+
+} // namespace Internal
+} // namespace Android
+
+#endif // ANDROIDEXTRALIBRARYLISTMODEL_H
diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp
index 7049a9ed7fc..ee27f181c5e 100644
--- a/src/plugins/qt4projectmanager/qt4nodes.cpp
+++ b/src/plugins/qt4projectmanager/qt4nodes.cpp
@@ -1999,6 +1999,7 @@ void Qt4ProFileNode::applyEvaluate(EvalResult evalResult, bool async)
         newVarValues[AndroidArchVar] = m_readerExact->values(QLatin1String("ANDROID_TARGET_ARCH"));
         newVarValues[AndroidDeploySettingsFile] = m_readerExact->values(QLatin1String("ANDROID_DEPLOYMENT_SETTINGS_FILE"));
         newVarValues[AndroidPackageSourceDir] = m_readerExact->values(QLatin1String("ANDROID_PACKAGE_SOURCE_DIR"));
+        newVarValues[AndroidExtraLibs] = m_readerExact->values(QLatin1String("ANDROID_EXTRA_LIBS"));
 
         m_isDeployable = false;
         if (m_projectType == ApplicationTemplate) {
diff --git a/src/plugins/qt4projectmanager/qt4nodes.h b/src/plugins/qt4projectmanager/qt4nodes.h
index 14f3cb032e4..c7b9d4890a4 100644
--- a/src/plugins/qt4projectmanager/qt4nodes.h
+++ b/src/plugins/qt4projectmanager/qt4nodes.h
@@ -107,7 +107,8 @@ enum Qt4Variable {
     ShLibExtensionVar,
     AndroidArchVar,
     AndroidDeploySettingsFile,
-    AndroidPackageSourceDir
+    AndroidPackageSourceDir,
+    AndroidExtraLibs
 };
 
 // Import base classes into namespace
-- 
GitLab