diff --git a/src/plugins/android/adbcommandswidget.cpp b/src/plugins/android/adbcommandswidget.cpp new file mode 100644 index 00000000000..a6f35877644 --- /dev/null +++ b/src/plugins/android/adbcommandswidget.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** 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 "adbcommandswidget.h" +#include "ui_adbcommandswidget.h" + +#include "utils/utilsicons.h" + +#include +#include +#include +#include + +namespace Android { +namespace Internal { + +using namespace std::placeholders; + +const char defaultCommand[] = "echo \"shell command\""; + +static void swapData(QStringListModel *model, const QModelIndex &srcIndex, + const QModelIndex &destIndex) +{ + if (model) { + QVariant data = model->data(destIndex, Qt::EditRole); // QTBUG-55078 + model->setData(destIndex, model->data(srcIndex, Qt::EditRole)); + model->setData(srcIndex, data); + } +} + +class AdbCommandsWidgetPrivate +{ +public: + AdbCommandsWidgetPrivate(const AdbCommandsWidget &parent, QWidget *parentWidget); + ~AdbCommandsWidgetPrivate(); + +private: + void addString(const QString &str); + void onAddButton(); + void onMoveUpButton(); + void onMoveDownButton(); + void onRemove(); + void onCurrentIndexChanged(const QModelIndex &newIndex, const QModelIndex &prevIndex); + + const AdbCommandsWidget &m_parent; + QGroupBox *m_rootWidget = nullptr; + Ui::AdbCommandsWidget *m_ui = nullptr; + QStringListModel *m_stringModel = nullptr; + friend class AdbCommandsWidget; +}; + +AdbCommandsWidget::AdbCommandsWidget(QWidget *parent) : + QObject(parent), + d(new AdbCommandsWidgetPrivate(*this, parent)) +{ +} + +AdbCommandsWidget::~AdbCommandsWidget() +{ +} + +QStringList AdbCommandsWidget::commandsList() const +{ + return d->m_stringModel->stringList(); +} + +QWidget *AdbCommandsWidget::widget() const +{ + return d->m_rootWidget; +} + +void AdbCommandsWidget::setTitleText(const QString &title) +{ + d->m_rootWidget->setTitle(title); +} + +void AdbCommandsWidget::setCommandList(const QStringList &commands) +{ + d->m_stringModel->setStringList(commands); +} + +AdbCommandsWidgetPrivate::AdbCommandsWidgetPrivate(const AdbCommandsWidget &parent, + QWidget *parentWidget): + m_parent(parent), + m_rootWidget(new QGroupBox(parentWidget)), + m_ui(new Ui::AdbCommandsWidget), + m_stringModel(new QStringListModel) +{ + m_ui->setupUi(m_rootWidget); + m_ui->addButton->setIcon(Utils::Icons::PLUS.icon()); + m_ui->removeButton->setIcon(Utils::Icons::MINUS.icon()); + m_ui->moveUpButton->setIcon(Utils::Icons::ARROW_UP.icon()); + m_ui->moveDownButton->setIcon(Utils::Icons::ARROW_DOWN.icon()); + + auto deleteShortcut = new QShortcut(QKeySequence(QKeySequence::Delete), m_ui->commandsView); + deleteShortcut->setContext(Qt::WidgetShortcut); + QObject::connect(deleteShortcut, &QShortcut::activated, + std::bind(&AdbCommandsWidgetPrivate::onRemove, this)); + + QObject::connect(m_ui->addButton, &QToolButton::clicked, + std::bind(&AdbCommandsWidgetPrivate::onAddButton, this)); + QObject::connect(m_ui->removeButton, &QToolButton::clicked, + std::bind(&AdbCommandsWidgetPrivate::onRemove, this)); + QObject::connect(m_ui->moveUpButton, &QToolButton::clicked, + std::bind(&AdbCommandsWidgetPrivate::onMoveUpButton, this)); + QObject::connect(m_ui->moveDownButton, &QToolButton::clicked, + std::bind(&AdbCommandsWidgetPrivate::onMoveDownButton, this)); + + m_ui->commandsView->setModel(m_stringModel); + QObject::connect(m_stringModel, &QStringListModel::dataChanged, + &m_parent, &AdbCommandsWidget::commandsChanged); + QObject::connect(m_stringModel, &QStringListModel::rowsRemoved, + &m_parent, &AdbCommandsWidget::commandsChanged); + QObject::connect(m_ui->commandsView->selectionModel(), &QItemSelectionModel::currentChanged, + std::bind(&AdbCommandsWidgetPrivate::onCurrentIndexChanged, this, _1, _2)); +} + +AdbCommandsWidgetPrivate::~AdbCommandsWidgetPrivate() +{ + delete m_ui; + delete m_stringModel; +} + +void AdbCommandsWidgetPrivate::addString(const QString &str) +{ + if (!str.isEmpty()) { + m_stringModel->insertRows(m_stringModel->rowCount(), 1); + const QModelIndex lastItemIndex = m_stringModel->index(m_stringModel->rowCount() - 1); + m_stringModel->setData(lastItemIndex, str); + } +} + +void AdbCommandsWidgetPrivate::onAddButton() +{ + addString(defaultCommand); + const QModelIndex index = m_stringModel->index(m_stringModel->rowCount() - 1); + m_ui->commandsView->setCurrentIndex(index); + m_ui->commandsView->edit(index); +} + +void AdbCommandsWidgetPrivate::onMoveUpButton() +{ + QModelIndex index = m_ui->commandsView->currentIndex(); + if (index.row() > 0) { + const QModelIndex newIndex = m_stringModel->index(index.row() - 1, 0); + swapData(m_stringModel, index, newIndex); + m_ui->commandsView->setCurrentIndex(newIndex); + } +} + +void AdbCommandsWidgetPrivate::onMoveDownButton() +{ + QModelIndex index = m_ui->commandsView->currentIndex(); + if (index.row() < m_stringModel->rowCount() - 1) { + const QModelIndex newIndex = m_stringModel->index(index.row() + 1, 0); + swapData(m_stringModel, index, newIndex); + m_ui->commandsView->setCurrentIndex(newIndex); + } +} + +void AdbCommandsWidgetPrivate::onRemove() +{ + const QModelIndex &index = m_ui->commandsView->currentIndex(); + if (index.isValid()) + m_stringModel->removeRow(index.row()); +} + +void AdbCommandsWidgetPrivate::onCurrentIndexChanged(const QModelIndex &newIndex, const QModelIndex &prevIndex) +{ + Q_UNUSED(prevIndex) + m_ui->moveUpButton->setEnabled(newIndex.row() != 0); + m_ui->moveDownButton->setEnabled(newIndex.row() < m_stringModel->rowCount() - 1); + m_ui->removeButton->setEnabled(newIndex.isValid()); +} + +} // Internal +} // Android diff --git a/src/plugins/android/adbcommandswidget.h b/src/plugins/android/adbcommandswidget.h new file mode 100644 index 00000000000..a1404049d04 --- /dev/null +++ b/src/plugins/android/adbcommandswidget.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** 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 +#include + +#include + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +namespace Android { +namespace Internal { + +class AdbCommandsWidgetPrivate; + +class AdbCommandsWidget : public QObject +{ + Q_OBJECT +public: + explicit AdbCommandsWidget(QWidget *parent); + ~AdbCommandsWidget(); + + QStringList commandsList() const; + void setCommandList(const QStringList &commands); + + QWidget *widget() const; + + void setTitleText(const QString &title); + +signals: + void commandsChanged(); + +private: + std::unique_ptr d; +}; + +} // Internal +} // Android diff --git a/src/plugins/android/adbcommandswidget.ui b/src/plugins/android/adbcommandswidget.ui new file mode 100644 index 00000000000..3aff5e4fea1 --- /dev/null +++ b/src/plugins/android/adbcommandswidget.ui @@ -0,0 +1,117 @@ + + + AdbCommandsWidget + + + + 0 + 0 + 682 + 391 + + + + Widget + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Qt::MoveAction + + + QListView::Snap + + + + + + + false + + + + + + Qt::NoArrow + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + false + + + + + + Qt::NoArrow + + + + + + + false + + + + + + + + + + + commandsView + addButton + removeButton + moveUpButton + moveDownButton + + + + diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index 702bb4d7cf7..d4bbcb95d39 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -50,7 +50,8 @@ HEADERS += \ androidtoolmanager.h \ androidsdkmanager.h \ androidavdmanager.h \ - androidrunconfigurationwidget.h + androidrunconfigurationwidget.h \ + adbcommandswidget.h SOURCES += \ androidconfigurations.cpp \ @@ -94,7 +95,8 @@ SOURCES += \ androidtoolmanager.cpp \ androidsdkmanager.cpp \ androidavdmanager.cpp \ - androidrunconfigurationwidget.cpp + androidrunconfigurationwidget.cpp \ + adbcommandswidget.cpp FORMS += \ androidsettingswidget.ui \ @@ -103,7 +105,8 @@ FORMS += \ androiddevicedialog.ui \ androiddeployqtwidget.ui \ androidbuildapkwidget.ui \ - androidrunconfigurationwidget.ui + androidrunconfigurationwidget.ui \ + adbcommandswidget.ui RESOURCES = android.qrc diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs index fcc2e9e52ee..af7106439f3 100644 --- a/src/plugins/android/android.qbs +++ b/src/plugins/android/android.qbs @@ -18,8 +18,11 @@ Project { files: [ "android_global.h", - "addnewavddialog.ui", "android.qrc", + "adbcommandswidget.cpp", + "adbcommandswidget.h", + "adbcommandswidget.ui", + "addnewavddialog.ui", "androidanalyzesupport.cpp", "androidanalyzesupport.h", "androidavdmanager.cpp", diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp index 7b69ed2364f..79bfa1007fa 100644 --- a/src/plugins/android/androidrunconfiguration.cpp +++ b/src/plugins/android/androidrunconfiguration.cpp @@ -40,7 +40,10 @@ using namespace ProjectExplorer; namespace Android { using namespace Internal; + const char amStartArgsKey[] = "Android.AmStartArgsKey"; +const char preStartShellCmdsKey[] = "Android.PreStartShellCmdListKey"; +const char postFinishShellCmdsKey[] = "Android.PostFinishShellCmdListKey"; AndroidRunConfiguration::AndroidRunConfiguration(Target *parent, Core::Id id) : RunConfiguration(parent, id) @@ -52,6 +55,16 @@ AndroidRunConfiguration::AndroidRunConfiguration(Target *parent, AndroidRunConfi { } +void AndroidRunConfiguration::setPreStartShellCommands(const QStringList &cmdList) +{ + m_preStartShellCommands = cmdList; +} + +void AndroidRunConfiguration::setPostFinishShellCommands(const QStringList &cmdList) +{ + m_postFinishShellCommands = cmdList; +} + void AndroidRunConfiguration::setAmStartExtraArgs(const QStringList &args) { m_amStartExtraArgs = args; @@ -61,8 +74,14 @@ QWidget *AndroidRunConfiguration::createConfigurationWidget() { auto configWidget = new AndroidRunConfigurationWidget(); configWidget->setAmStartArgs(m_amStartExtraArgs); + configWidget->setPreStartShellCommands(m_preStartShellCommands); + configWidget->setPostFinishShellCommands(m_postFinishShellCommands); connect(configWidget, &AndroidRunConfigurationWidget::amStartArgsChanged, this, &AndroidRunConfiguration::setAmStartExtraArgs); + connect(configWidget, &AndroidRunConfigurationWidget::preStartCmdsChanged, + this, &AndroidRunConfiguration::setPreStartShellCommands); + connect(configWidget, &AndroidRunConfigurationWidget::postFinishCmdsChanged, + this, &AndroidRunConfiguration::setPostFinishShellCommands); return configWidget; } @@ -73,6 +92,8 @@ Utils::OutputFormatter *AndroidRunConfiguration::createOutputFormatter() const bool AndroidRunConfiguration::fromMap(const QVariantMap &map) { + m_preStartShellCommands = map.value(preStartShellCmdsKey).toStringList(); + m_postFinishShellCommands = map.value(postFinishShellCmdsKey).toStringList(); m_amStartExtraArgs = map.value(amStartArgsKey).toStringList(); return RunConfiguration::fromMap(map); } @@ -80,6 +101,8 @@ bool AndroidRunConfiguration::fromMap(const QVariantMap &map) QVariantMap AndroidRunConfiguration::toMap() const { QVariantMap res = RunConfiguration::toMap(); + res[preStartShellCmdsKey] = m_preStartShellCommands; + res[postFinishShellCmdsKey] = m_postFinishShellCommands; res[amStartArgsKey] = m_amStartExtraArgs; return res; } @@ -88,4 +111,15 @@ const QStringList &AndroidRunConfiguration::amStartExtraArgs() const { return m_amStartExtraArgs; } + +const QStringList &AndroidRunConfiguration::preStartShellCommands() const +{ + return m_preStartShellCommands; +} + +const QStringList &AndroidRunConfiguration::postFinishShellCommands() const +{ + return m_postFinishShellCommands; +} + } // namespace Android diff --git a/src/plugins/android/androidrunconfiguration.h b/src/plugins/android/androidrunconfiguration.h index 3ba7d25d7db..45b00d8a845 100644 --- a/src/plugins/android/androidrunconfiguration.h +++ b/src/plugins/android/androidrunconfiguration.h @@ -48,15 +48,21 @@ public: QVariantMap toMap() const override; const QStringList &amStartExtraArgs() const; + const QStringList &preStartShellCommands() const; + const QStringList &postFinishShellCommands() const; protected: AndroidRunConfiguration(ProjectExplorer::Target *parent, AndroidRunConfiguration *source); private: + void setPreStartShellCommands(const QStringList &cmdList); + void setPostFinishShellCommands(const QStringList &cmdList); void setAmStartExtraArgs(const QStringList &args); private: QStringList m_amStartExtraArgs; + QStringList m_preStartShellCommands; + QStringList m_postFinishShellCommands; }; } // namespace Android diff --git a/src/plugins/android/androidrunconfigurationwidget.cpp b/src/plugins/android/androidrunconfigurationwidget.cpp index ba4d038408d..a9560fdaaa1 100644 --- a/src/plugins/android/androidrunconfigurationwidget.cpp +++ b/src/plugins/android/androidrunconfigurationwidget.cpp @@ -23,6 +23,7 @@ ** ****************************************************************************/ #include "androidrunconfigurationwidget.h" +#include "adbcommandswidget.h" #include "ui_androidrunconfigurationwidget.h" #include "utils/utilsicons.h" @@ -39,6 +40,26 @@ AndroidRunConfigurationWidget::AndroidRunConfigurationWidget(QWidget *parent): m_ui->setupUi(detailsWidget); m_ui->m_warningIconLabel->setPixmap(Utils::Icons::WARNING.pixmap()); + m_preStartCmdsWidget = new AdbCommandsWidget(detailsWidget); + connect(m_preStartCmdsWidget, &AdbCommandsWidget::commandsChanged, [this]() { + emit preStartCmdsChanged(m_preStartCmdsWidget->commandsList()); + }); + m_preStartCmdsWidget->setTitleText(tr("Shell commands to run on Android device before" + " application launch.")); + + m_postEndCmdsWidget = new AdbCommandsWidget(detailsWidget); + connect(m_postEndCmdsWidget, &AdbCommandsWidget::commandsChanged, [this]() { + emit postFinishCmdsChanged(m_postEndCmdsWidget->commandsList()); + }); + m_postEndCmdsWidget->setTitleText(tr("Shell commands to run on Android device after application" + " quits.")); + + auto mainLayout = static_cast(detailsWidget->layout()); + mainLayout->addWidget(m_preStartCmdsWidget->widget(), mainLayout->rowCount(), + 0, mainLayout->columnCount() - 1, 0); + mainLayout->addWidget(m_postEndCmdsWidget->widget(), mainLayout->rowCount(), + 0, mainLayout->columnCount() - 1, 0); + setWidget(detailsWidget); setSummaryText(tr("Android run settings")); @@ -54,8 +75,17 @@ AndroidRunConfigurationWidget::~AndroidRunConfigurationWidget() void AndroidRunConfigurationWidget::setAmStartArgs(const QStringList &args) { - if (m_ui->m_amStartArgsEdit && !args.isEmpty()) - m_ui->m_amStartArgsEdit->setText(Utils::QtcProcess::joinArgs(args, Utils::OsTypeLinux)); + m_ui->m_amStartArgsEdit->setText(Utils::QtcProcess::joinArgs(args, Utils::OsTypeLinux)); +} + +void AndroidRunConfigurationWidget::setPreStartShellCommands(const QStringList &cmdList) +{ + m_preStartCmdsWidget->setCommandList(cmdList); +} + +void AndroidRunConfigurationWidget::setPostFinishShellCommands(const QStringList &cmdList) +{ + m_postEndCmdsWidget->setCommandList(cmdList); } } // namespace Internal diff --git a/src/plugins/android/androidrunconfigurationwidget.h b/src/plugins/android/androidrunconfigurationwidget.h index 14d1d923dbb..249e00a672c 100644 --- a/src/plugins/android/androidrunconfigurationwidget.h +++ b/src/plugins/android/androidrunconfigurationwidget.h @@ -29,10 +29,14 @@ #include "projectexplorer/runconfiguration.h" #include "utils/detailswidget.h" +#include + +#include namespace Android { namespace Internal { +class AdbCommandsWidget; namespace Ui { class AndroidRunConfigurationWidget; } @@ -45,12 +49,22 @@ public: ~AndroidRunConfigurationWidget(); void setAmStartArgs(const QStringList &args); + void setPreStartShellCommands(const QStringList &cmdList); + void setPostFinishShellCommands(const QStringList &cmdList); signals: void amStartArgsChanged(QStringList args); + void preStartCmdsChanged(const QStringList &cmdList); + void postFinishCmdsChanged(const QStringList &cmdList); + +private: + void addPreStartCommand(const QString &command); + void addPostFinishCommand(const QString &command); private: std::unique_ptr m_ui; + AdbCommandsWidget *m_preStartCmdsWidget = nullptr; + AdbCommandsWidget *m_postEndCmdsWidget = nullptr; }; } // namespace Internal diff --git a/src/plugins/android/androidrunconfigurationwidget.ui b/src/plugins/android/androidrunconfigurationwidget.ui index cd7ed3df801..5cfee96dc89 100644 --- a/src/plugins/android/androidrunconfigurationwidget.ui +++ b/src/plugins/android/androidrunconfigurationwidget.ui @@ -6,25 +6,31 @@ 0 0 - 625 - 73 + 731 + 119 + + + 0 + 0 + + Form - - - - - + + -1 + + + - Conflicting "am start" options might result in the app startup failure. + Activity manager start options: - + @@ -37,7 +43,7 @@ - + Qt::Horizontal @@ -53,13 +59,32 @@ - - + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 12 + + + + + + - Activity manager start options: + Conflicting "am start" options might result in the app startup failure. + + + diff --git a/src/plugins/android/androidrunnable.h b/src/plugins/android/androidrunnable.h index 89ca03d1361..9839dada07b 100644 --- a/src/plugins/android/androidrunnable.h +++ b/src/plugins/android/androidrunnable.h @@ -37,8 +37,8 @@ struct ANDROID_EXPORT AndroidRunnable QString intentName; QStringList amStartExtraArgs; Utils::Environment environment; - QVector beforeStartADBCommands; - QVector afterFinishADBCommands; + QStringList beforeStartAdbCommands; + QStringList afterFinishAdbCommands; QString deviceSerialNumber; QString displayName() const { return packageName; } @@ -51,8 +51,8 @@ inline bool operator==(const AndroidRunnable &r1, const AndroidRunnable &r2) && r1.intentName == r2.intentName && r1.amStartExtraArgs == r2.amStartExtraArgs && r1.environment == r2.environment - && r1.beforeStartADBCommands == r2.beforeStartADBCommands - && r1.afterFinishADBCommands == r2.afterFinishADBCommands + && r1.beforeStartAdbCommands == r2.beforeStartAdbCommands + && r1.afterFinishAdbCommands == r2.afterFinishAdbCommands && r1.deviceSerialNumber == r2.deviceSerialNumber; } diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index 4a62e028fe9..9b9c8578abb 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -214,14 +214,13 @@ class AndroidRunnerWorker : public QObject }; public: - AndroidRunnerWorker(RunControl *runControl, const QString &packageName, - const QStringList &selector); + AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable); ~AndroidRunnerWorker(); - void asyncStart(const AndroidRunnable &runnable); - void asyncStop(const AndroidRunnable &runnable); + void asyncStart(); + void asyncStop(); - void setAdbParameters(const QString &packageName, const QStringList &selector); + void setAndroidRunnable(const AndroidRunnable &runnable); void handleRemoteDebuggerRunning(); Utils::Port localGdbServerPort() const { return m_localGdbServerPort; } @@ -238,7 +237,7 @@ private: void logcatReadStandardError(); void logcatReadStandardOutput(); void adbKill(qint64 pid); - QStringList selector() const { return m_selector; } + QStringList selector() const; void forceStop(); void findPs(); void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError); @@ -264,22 +263,19 @@ private: QString m_gdbserverPath; QString m_gdbserverSocket; QString m_adb; - QStringList m_selector; QRegExp m_logCatRegExp; DebugHandShakeType m_handShakeMethod = SocketHandShake; bool m_customPort = false; - QString m_packageName; + AndroidRunnable m_androidRunnable; int m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT; }; -AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const QString &packageName, - const QStringList &selector) +AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable) : m_adbLogcatProcess(nullptr, deleter) , m_psIsAlive(nullptr, deleter) - , m_selector(selector) , m_logCatRegExp(regExpLogcat) - , m_packageName(packageName) + , m_androidRunnable(runnable) { auto runConfig = runControl->runConfiguration(); auto aspect = runConfig->extraAspect(); @@ -307,9 +303,9 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const QString & } m_adb = AndroidConfigurations::currentConfig().adbToolPath().toString(); - QString packageDir = "/data/data/" + m_packageName; + QString packageDir = "/data/data/" + m_androidRunnable.packageName; m_pingFile = packageDir + "/debug-ping"; - m_pongFile = "/data/local/tmp/qt/debug-pong-" + m_packageName; + m_pongFile = "/data/local/tmp/qt/debug-pong-" + m_androidRunnable.packageName; m_gdbserverSocket = packageDir + "/debug-socket"; const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion( runConfig->target()->kit()); @@ -347,20 +343,20 @@ AndroidRunnerWorker::~AndroidRunnerWorker() void AndroidRunnerWorker::forceStop() { - runAdb({"shell", "am", "force-stop", m_packageName}, nullptr, 30); + runAdb({"shell", "am", "force-stop", m_androidRunnable.packageName}, nullptr, 30); // try killing it via kill -9 const QByteArray out = Utils::SynchronousProcess() .runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScript) .allRawOutput(); - qint64 pid = extractPID(out.simplified(), m_packageName); + qint64 pid = extractPID(out.simplified(), m_androidRunnable.packageName); if (pid != -1) { adbKill(pid); } } -void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable) +void AndroidRunnerWorker::asyncStart() { forceStop(); @@ -378,12 +374,12 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable) if (m_useCppDebugger) runAdb({"shell", "rm", m_pongFile}); // Remove pong file. - for (const QStringList &entry: runnable.beforeStartADBCommands) - runAdb(entry); + for (const QString &entry: m_androidRunnable.beforeStartAdbCommands) + runAdb(entry.split(' ', QString::SkipEmptyParts)); QStringList args({"shell", "am", "start"}); - args << runnable.amStartExtraArgs; - args << "-n" << runnable.intentName; + args << m_androidRunnable.amStartExtraArgs; + args << "-n" << m_androidRunnable.intentName; if (m_useCppDebugger) { if (!runAdb({"forward", "--remove", "tcp:" + m_localGdbServerPort.toString()})){ @@ -395,7 +391,7 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable) return; } - const QString pingPongSocket(m_packageName + ".ping_pong_socket"); + const QString pingPongSocket(m_androidRunnable.packageName + ".ping_pong_socket"); args << "-e" << "debug_ping" << "true"; if (m_handShakeMethod == SocketHandShake) { args << "-e" << "ping_socket" << pingPongSocket; @@ -499,7 +495,8 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable) break; if (i == 20) { - emit remoteProcessFinished(tr("Unable to start \"%1\".").arg(m_packageName)); + emit remoteProcessFinished(tr("Unable to start \"%1\".") + .arg(m_androidRunnable.packageName)); return; } qDebug() << "WAITING FOR " << tmp.fileName(); @@ -512,7 +509,7 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable) QTC_ASSERT(!m_adbLogcatProcess, /**/); m_adbLogcatProcess = std::move(logcatProcess); m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(), - m_packageName), + m_androidRunnable.packageName), bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1)); } @@ -543,7 +540,7 @@ bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *exitMessage, { Utils::SynchronousProcess adb; adb.setTimeoutS(timeoutS); - Utils::SynchronousProcessResponse response = adb.run(m_adb, m_selector + args); + Utils::SynchronousProcessResponse response = adb.run(m_adb, selector() + args); if (exitMessage) *exitMessage = response.exitMessage(m_adb, timeoutS); return response.result == Utils::SynchronousProcessResponse::Finished; @@ -567,7 +564,7 @@ void AndroidRunnerWorker::handleRemoteDebuggerRunning() // emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort); } -void AndroidRunnerWorker::asyncStop(const AndroidRunnable &runnable) +void AndroidRunnerWorker::asyncStop() { if (!m_pidFinder.isFinished()) m_pidFinder.cancel(); @@ -575,14 +572,11 @@ void AndroidRunnerWorker::asyncStop(const AndroidRunnable &runnable) if (m_processPID != -1) { forceStop(); } - for (const QStringList &entry: runnable.afterFinishADBCommands) - runAdb(entry); } -void AndroidRunnerWorker::setAdbParameters(const QString &packageName, const QStringList &selector) +void AndroidRunnerWorker::setAndroidRunnable(const AndroidRunnable &runnable) { - m_packageName = packageName; - m_selector = selector; + m_androidRunnable = runnable; } void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError) @@ -635,10 +629,14 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) m_processPID = pid; if (pid == -1) { emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.") - .arg(m_packageName)); + .arg(m_androidRunnable.packageName)); // App died/killed. Reset log and monitor processes. m_adbLogcatProcess.reset(); m_psIsAlive.reset(); + + // Run adb commands after application quit. + for (const QString &entry: m_androidRunnable.afterFinishAdbCommands) + runAdb(entry.split(' ', QString::SkipEmptyParts)); } else { // In debugging cases this will be funneled to the engine to actually start // and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below. @@ -671,7 +669,12 @@ void AndroidRunnerWorker::logcatReadStandardOutput() void AndroidRunnerWorker::adbKill(qint64 pid) { runAdb({"shell", "kill", "-9", QString::number(pid)}); - runAdb({"shell", "run-as", m_packageName, "kill", "-9", QString::number(pid)}); + runAdb({"shell", "run-as", m_androidRunnable.packageName, "kill", "-9", QString::number(pid)}); +} + +QStringList AndroidRunnerWorker::selector() const +{ + return AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber); } AndroidRunner::AndroidRunner(RunControl *runControl) @@ -694,15 +697,19 @@ AndroidRunner::AndroidRunner(RunControl *runControl) auto androidRunConfig = qobject_cast(runControl->runConfiguration()); m_androidRunnable.amStartExtraArgs = androidRunConfig->amStartExtraArgs(); + for (QString shellCmd: androidRunConfig->preStartShellCommands()) + m_androidRunnable.beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd)); - m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable.packageName, - AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber))); + for (QString shellCmd: androidRunConfig->postFinishShellCommands()) + m_androidRunnable.afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd)); + + m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable)); m_worker->moveToThread(&m_thread); connect(this, &AndroidRunner::asyncStart, m_worker.data(), &AndroidRunnerWorker::asyncStart); connect(this, &AndroidRunner::asyncStop, m_worker.data(), &AndroidRunnerWorker::asyncStop); - connect(this, &AndroidRunner::adbParametersChanged, - m_worker.data(), &AndroidRunnerWorker::setAdbParameters); + connect(this, &AndroidRunner::androidRunnableChanged, + m_worker.data(), &AndroidRunnerWorker::setAndroidRunnable); connect(this, &AndroidRunner::remoteDebuggerRunning, m_worker.data(), &AndroidRunnerWorker::handleRemoteDebuggerRunning); @@ -738,7 +745,7 @@ void AndroidRunner::start() } } - emit asyncStart(m_androidRunnable); + emit asyncStart(); } void AndroidRunner::stop() @@ -750,7 +757,7 @@ void AndroidRunner::stop() return; } - emit asyncStop(m_androidRunnable); + emit asyncStop(); } void AndroidRunner::qmlServerPortReady(Port port) @@ -797,8 +804,7 @@ void AndroidRunner::setRunnable(const AndroidRunnable &runnable) { if (runnable != m_androidRunnable) { m_androidRunnable = runnable; - emit adbParametersChanged(runnable.packageName, - AndroidDeviceInfo::adbSelector(runnable.deviceSerialNumber)); + emit androidRunnableChanged(m_androidRunnable); } } @@ -816,8 +822,7 @@ void AndroidRunner::launchAVD() AndroidConfigurations::None); AndroidManager::setDeviceSerialNumber(m_target, info.serialNumber); m_androidRunnable.deviceSerialNumber = info.serialNumber; - emit adbParametersChanged(m_androidRunnable.packageName, - AndroidDeviceInfo::adbSelector(info.serialNumber)); + emit androidRunnableChanged(m_androidRunnable); if (info.isValid()) { AndroidAvdManager avdManager; if (avdManager.findAvd(info.avdname).isEmpty()) { @@ -840,7 +845,7 @@ void AndroidRunner::checkAVD() if (avdManager.isAvdBooted(serialNumber)) { m_checkAVDTimer.stop(); AndroidManager::setDeviceSerialNumber(m_target, serialNumber); - emit asyncStart(m_androidRunnable); + emit asyncStart(); } else if (!config.isConnected(serialNumber)) { // device was disconnected m_checkAVDTimer.stop(); diff --git a/src/plugins/android/androidrunner.h b/src/plugins/android/androidrunner.h index cf24d8423bf..2efd52299cf 100644 --- a/src/plugins/android/androidrunner.h +++ b/src/plugins/android/androidrunner.h @@ -64,12 +64,11 @@ public: void stop() override; signals: - void asyncStart(const AndroidRunnable &runnable); - void asyncStop(const AndroidRunnable &runnable); + void asyncStart(); + void asyncStop(); void remoteDebuggerRunning(); void qmlServerReady(const QUrl &serverUrl); - - void adbParametersChanged(const QString &packageName, const QStringList &selector); + void androidRunnableChanged(const AndroidRunnable &runnable); void avdDetected(); private: