Android: Let user specify shell commands

Let user specify list of shell commands to run before app starts
and after app quits.

Change-Id: I9794fb96180530ca6c28ce6581fda51a25be28d4
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Vikas Pachdha
2017-06-08 14:57:09 +02:00
parent cc8bff67b3
commit a6a13bfb76
13 changed files with 569 additions and 71 deletions

View File

@@ -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 <QGroupBox>
#include <QItemSelectionModel>
#include <QShortcut>
#include <QStringListModel>
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

View File

@@ -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 <QObject>
#include <QStringList>
#include <memory>
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<AdbCommandsWidgetPrivate> d;
};
} // Internal
} // Android

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AdbCommandsWidget</class>
<widget class="QGroupBox" name="AdbCommandsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>682</width>
<height>391</height>
</rect>
</property>
<property name="windowTitle">
<string>Widget</string>
</property>
<property name="title">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" rowspan="6">
<widget class="QListView" name="commandsView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="movement">
<enum>QListView::Snap</enum>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QToolButton" name="moveUpButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
<item row="5" column="1">
<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>
<item row="0" column="1">
<widget class="QToolButton" name="addButton">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QToolButton" name="moveDownButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="removeButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>commandsView</tabstop>
<tabstop>addButton</tabstop>
<tabstop>removeButton</tabstop>
<tabstop>moveUpButton</tabstop>
<tabstop>moveDownButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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<QGridLayout*>(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

View File

@@ -29,10 +29,14 @@
#include "projectexplorer/runconfiguration.h"
#include "utils/detailswidget.h"
#include <QStringListModel>
#include <memory>
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<Ui::AndroidRunConfigurationWidget> m_ui;
AdbCommandsWidget *m_preStartCmdsWidget = nullptr;
AdbCommandsWidget *m_postEndCmdsWidget = nullptr;
};
} // namespace Internal

View File

@@ -6,25 +6,31 @@
<rect>
<x>0</x>
<y>0</y>
<width>625</width>
<height>73</height>
<width>731</width>
<height>119</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QLineEdit" name="m_amStartArgsEdit"/>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_3">
<property name="spacing">
<number>-1</number>
</property>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Conflicting &quot;am start&quot; options might result in the app startup failure.</string>
<string>Activity manager start options:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QLabel" name="m_warningIconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
@@ -37,7 +43,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -53,13 +59,32 @@
</property>
</spacer>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label">
<item row="3" column="0" colspan="3">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>12</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Activity manager start options:</string>
<string>Conflicting "am start" options might result in the app startup failure.</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="m_amStartArgsEdit"/>
</item>
</layout>
</widget>
<resources/>

View File

@@ -37,8 +37,8 @@ struct ANDROID_EXPORT AndroidRunnable
QString intentName;
QStringList amStartExtraArgs;
Utils::Environment environment;
QVector<QStringList> beforeStartADBCommands;
QVector<QStringList> 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;
}

View File

@@ -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<Debugger::DebuggerRunConfigurationAspect>();
@@ -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<AndroidRunConfiguration *>(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();

View File

@@ -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: