diff options
author | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2021-09-23 17:11:19 +0300 |
---|---|---|
committer | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2021-09-30 09:06:23 +0000 |
commit | b7c15d4d8dfdbc40c89fc6ba3c6e9e7d142cdab4 (patch) | |
tree | 9390dd6a7207b8dbc5f1be79d1f0a88743d7bee4 /src/plugins/android | |
parent | b1d5abd8eb6f1b7ee7a56854c2dd841b89efa37c (diff) |
Android: Make fixes to androidqmlpreview
This amends 261a39cbbd2fa53d35bd4d4de8642dc341f9f6ad with fixes
to issues noticed after merging the initial patch.
Change-Id: I5f859374cbba3a2e020e6ca0789cc2b387d2739a
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'src/plugins/android')
-rw-r--r-- | src/plugins/android/androidqmlpreviewworker.cpp | 354 | ||||
-rw-r--r-- | src/plugins/android/androidqmlpreviewworker.h | 32 |
2 files changed, 180 insertions, 206 deletions
diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp index 645cabbb48a..37de94a6f94 100644 --- a/src/plugins/android/androidqmlpreviewworker.cpp +++ b/src/plugins/android/androidqmlpreviewworker.cpp @@ -23,12 +23,13 @@ ** ****************************************************************************/ +#include "androidqmlpreviewworker.h" + #include "androidavdmanager.h" #include "androiddevice.h" #include "androiddeviceinfo.h" #include "androidglobal.h" #include "androidmanager.h" -#include "androidqmlpreviewworker.h" #include <coreplugin/icore.h> @@ -44,10 +45,10 @@ #include <qtsupport/baseqtversion.h> #include <qtsupport/qtkitinformation.h> +#include <utils/runextensions.h> + +#include <QDateTime> #include <QThread> -#include <QTemporaryDir> -#include <QImageReader> -#include <QtConcurrent> namespace Android { namespace Internal { @@ -80,173 +81,143 @@ ApkInfo::ApkInfo() : Q_GLOBAL_STATIC(ApkInfo, apkInfo) -const char packageSuffix[] = ".qmlrc"; +static const char packageSuffix[] = ".qmlrc"; -static inline bool isMainThread() +FilePath AndroidQmlPreviewWorker::designViewerApkPath(const QString &abi) const { - return QCoreApplication::instance()->thread() == QThread::currentThread(); -} - -static FilePath viewerApkPath(const QString &avdAbi) -{ - if (avdAbi.isEmpty()) + if (abi.isEmpty()) return {}; - if (apkInfo()->abis.contains(avdAbi)) - return Core::ICore::resourcePath(QString("android/qtdesignviewer/designviewer_%1.apk"). - arg(avdAbi)); + if (apkInfo()->abis.contains(abi)) { + return Core::ICore::resourcePath(QString("android/qtdesignviewer/designviewer_%1.apk") + .arg(abi)); + } return {}; } -static SdkToolResult runAdbCommandAsyncAndWait(const QString &dev, const QStringList &arguments) +SdkToolResult AndroidQmlPreviewWorker::runAdbCommand(const QStringList &arguments) const { QStringList args; - if (!dev.isEmpty()) - args << AndroidDeviceInfo::adbSelector(dev); + if (!m_serialNumber.isEmpty()) + args << AndroidDeviceInfo::adbSelector(m_serialNumber); args << arguments; - QFuture<SdkToolResult> asyncResult = QtConcurrent::run([args] { - return AndroidManager::runAdbCommand(args);}); + return AndroidManager::runAdbCommand(args); +} - while (asyncResult.isRunning()) { - QCoreApplication::instance()->processEvents(QEventLoop::AllEvents, 100); - } - return asyncResult.result(); +SdkToolResult AndroidQmlPreviewWorker::runAdbShellCommand(const QStringList &arguments) const +{ + return runAdbCommand(QStringList() << "shell" << arguments); } -static SdkToolResult runAdbCommand(const QString &dev, const QStringList &arguments) +int AndroidQmlPreviewWorker::pidofPreview() const { - if (isMainThread()) - return runAdbCommandAsyncAndWait(dev, arguments); - QStringList args; - if (!dev.isEmpty()) - args << AndroidDeviceInfo::adbSelector(dev); - args << arguments; - return AndroidManager::runAdbCommand(args); + const QStringList command{"pidof", apkInfo()->appId}; + const SdkToolResult res = runAdbShellCommand(command); + return res.success() ? res.stdOut().toInt() : -1; } -static SdkToolResult runAdbShellCommand(const QString &dev, const QStringList &arguments) +bool AndroidQmlPreviewWorker::isPreviewRunning(int lastKnownPid) const { - const QStringList shellCmd{"shell"}; - return runAdbCommand(dev, shellCmd + arguments); + const int pid = pidofPreview(); + return (lastKnownPid > 1) ? lastKnownPid == pid : pid > 1; } -static QString startAvd(const AndroidAvdManager &avd, const QString &name) +void AndroidQmlPreviewWorker::startPidWatcher() { - QFuture<QString> asyncRes = QtConcurrent::run([avd, name] { - return avd.startAvd(name); - }); - while (asyncRes.isRunning()) - QCoreApplication::instance()->processEvents(QEventLoop::AllEvents, 100); - return asyncRes.result(); + m_pidFutureWatcher.setFuture(Utils::runAsync([this]() { + // wait for started + const int sleepTimeMs = 2000; + QDeadlineTimer deadline(20000); + while (!m_pidFutureWatcher.isCanceled() && !deadline.hasExpired()) { + if (m_viewerPid == -1) { + m_viewerPid = pidofPreview(); + if (m_viewerPid > 0) { + emit previewPidChanged(); + break; + } + } + QThread::msleep(sleepTimeMs); + } + + while (!m_pidFutureWatcher.isCanceled()) { + if (!isPreviewRunning(m_viewerPid)) { + stop(); + break; + } + QThread::msleep(sleepTimeMs); + } + })); } -static int pidofPreview(const QString &dev) +void AndroidQmlPreviewWorker::startLogcat() { - const QStringList command{"pidof", apkInfo()->appId}; - const SdkToolResult res = runAdbShellCommand(dev, command); - return res.success() ? res.stdOut().toInt() : -1; + QString args = QString("logcat --pid=%1").arg(m_viewerPid); + if (!m_logcatStartTimeStamp.isEmpty()) + args += QString(" -T '%1'").arg(m_logcatStartTimeStamp); + Utils::CommandLine cmd(AndroidConfigurations::currentConfig().adbToolPath()); + cmd.setArguments(args); + m_logcatProcess.setCommand(cmd); + m_logcatProcess.setUseCtrlCStub(true); + m_logcatProcess.start(); } -static bool isPreviewRunning(const QString &dev, int lastKnownPid = -1) +void AndroidQmlPreviewWorker::filterLogcatAndAppendMessage(const QString &stdOut) { - const int pid = pidofPreview(dev); - return (lastKnownPid > 1) ? lastKnownPid == pid : pid > 1; + for (const QString &line : stdOut.split('\n')) { + QStringList splittedLine = line.split(QLatin1String("%1: ").arg(apkInfo()->name)); + if (splittedLine.count() == 1) + continue; + + const QString outLine = splittedLine.last(); + const QString firstPart = splittedLine.first(); + if (firstPart.contains(" I ") || firstPart.contains(" D ")) + appendMessage(outLine, NormalMessageFormat); + else + appendMessage(outLine, ErrorMessageFormat); + } } AndroidQmlPreviewWorker::AndroidQmlPreviewWorker(ProjectExplorer::RunControl *runControl) - : ProjectExplorer::RunWorker(runControl) - , m_rc(runControl) - , m_config(AndroidConfigurations::currentConfig()) + : ProjectExplorer::RunWorker(runControl), + m_rc(runControl), + m_androidConfig(AndroidConfigurations::currentConfig()) { + connect(this, &RunWorker::started, this, &AndroidQmlPreviewWorker::startPidWatcher); + connect(this, &RunWorker::stopped, &m_pidFutureWatcher, &QFutureWatcher<void>::cancel); + connect(this, &AndroidQmlPreviewWorker::previewPidChanged, + this, &AndroidQmlPreviewWorker::startLogcat); + + connect(this, &RunWorker::stopped, &m_logcatProcess, &Utils::QtcProcess::stopProcess); + m_logcatProcess.setStdOutCallback([this](const QString &stdOut) { + filterLogcatAndAppendMessage(stdOut); + }); } -QStringList filterAppLog(const QStringList& oldList, const QStringList& newList) +AndroidQmlPreviewWorker::~AndroidQmlPreviewWorker() { - QStringList list = Utils::filtered(newList, - [](const auto & arg){return arg.contains(apkInfo()->name);}); - for (const auto &oldEntry : oldList) { - list.removeAll(oldEntry); - } - return list; + m_pidFutureWatcher.cancel(); + m_pidFutureWatcher.waitForFinished(); } void AndroidQmlPreviewWorker::start() { - UploadInfo transfer; - const bool res = ensureAvdIsRunning() - && checkAndInstallPreviewApp() - && prepareUpload(transfer) - && uploadFiles(transfer) - && runPreviewApp(transfer); - - if (!res) { - reportFailure(); - return; + const SdkToolResult dateResult = runAdbCommand({"shell", "date", "+%s"}); + if (dateResult.success()) { + m_logcatStartTimeStamp = QDateTime::fromSecsSinceEpoch(dateResult.stdOut().toInt()) + .toString("MM-dd hh:mm:ss.mmm"); } - reportStarted(); - //Thread to monitor preview life - QtConcurrent::run([this]() { - QElapsedTimer timer; - timer.start(); - while (runControl() && runControl()->isRunning()) { - if (m_viewerPid == -1) { - m_viewerPid = pidofPreview(m_devInfo.serialNumber); - if (m_viewerPid > 0) - QMetaObject::invokeMethod(this, &AndroidQmlPreviewWorker::startLogcat); - } else if (timer.elapsed() > 2000) { - //Get the application output - if (!isPreviewRunning(m_devInfo.serialNumber, m_viewerPid)) - QMetaObject::invokeMethod(this, &AndroidQmlPreviewWorker::stop); - - timer.restart(); - } - QThread::msleep(100); - } - }); -} + const bool previewStarted = ensureAvdIsRunning() + && checkAndInstallPreviewApp() + && uploadPreviewArtefacts() + && preparePreviewArtefacts() + && startPreviewApp(); -void AndroidQmlPreviewWorker::startLogcat() -{ - QtConcurrent::run([this]() { - QElapsedTimer timer; - timer.start(); - int initialPid = m_viewerPid; // to check if our initial process is still alive - QStringList logLines; - auto appendLogLinesCall = [&logLines, this](){ appendLogLines(logLines); }; - auto runCondition = [this, initialPid](){ return (runControl() && runControl()->isRunning()) - && initialPid == m_viewerPid;}; - QString timeFilter; - while (runCondition()) { - if (timer.elapsed() > 2000) { - //Get the application output - QStringList logcatCmd = {"logcat", QString("--pid=%1").arg(initialPid), "-t"}; - if (!timeFilter.isEmpty()) - logcatCmd.append(QString("%1").arg(timeFilter)); - else - logcatCmd.append(QString("1000")); //show last 1000 lines (but for the 1st time) - - const SdkToolResult logcatResult = runAdbCommand(m_devInfo.serialNumber, logcatCmd); - if (runCondition()) { - const QStringList output = logcatResult.stdOut().split('\n'); - const QStringList filtered = filterAppLog(logLines, output); - - if (!filtered.isEmpty()){ - const QString lastLine = filtered.last(); - timeFilter = lastLine.left(lastLine.indexOf(" ", lastLine.indexOf(" ") + 1)); - QMetaObject::invokeMethod(this, appendLogLinesCall); - logLines = filtered; - } - } - timer.restart(); - } - QThread::msleep(100); - } - }); + previewStarted ? reportStarted() : reportStopped(); } void AndroidQmlPreviewWorker::stop() { - if (!isPreviewRunning(m_devInfo.serialNumber, m_viewerPid) || stopPreviewApp()) + if (!isPreviewRunning(m_viewerPid) || stopPreviewApp()) appendMessage(tr("%1 has been stopped.").arg(apkInfo()->name), NormalMessageFormat); m_viewerPid = -1; reportStopped(); @@ -254,28 +225,35 @@ void AndroidQmlPreviewWorker::stop() bool AndroidQmlPreviewWorker::ensureAvdIsRunning() { - AndroidAvdManager avdMan(m_config); + AndroidAvdManager avdMananager(m_androidConfig); QString devSN = AndroidManager::deviceSerialNumber(m_rc->target()); if (devSN.isEmpty()) - devSN = m_devInfo.serialNumber; + devSN = m_serialNumber; - if (!avdMan.isAvdBooted(devSN)) { - m_devInfo = {}; + if (!avdMananager.isAvdBooted(devSN)) { using namespace ProjectExplorer; const IDevice *dev = DeviceKitAspect::device(m_rc->target()->kit()).data(); + if (!dev) { + appendMessage(tr("Selected device is invalid."), ErrorMessageFormat); + return false; + } + if (dev->deviceState() == IDevice::DeviceDisconnected) { + appendMessage(tr("Selected device is disconnected."), ErrorMessageFormat); + return false; + } AndroidDeviceInfo devInfoLocal = AndroidDevice::androidDeviceInfoFromIDevice(dev); if (devInfoLocal.isValid()) { - if (devInfoLocal.type == AndroidDeviceInfo::Emulator) { + if (dev->machineType() == IDevice::Emulator) { appendMessage(tr("Launching AVD."), NormalMessageFormat); - devInfoLocal.serialNumber = startAvd(avdMan, devInfoLocal.avdname); + devInfoLocal.serialNumber = avdMananager.startAvd(devInfoLocal.avdname); } if (devInfoLocal.serialNumber.isEmpty()) { - appendMessage(tr("Could not run AVD."), ErrorMessageFormat); + appendMessage(tr("Could not start AVD."), ErrorMessageFormat); } else { - m_devInfo = devInfoLocal; - m_avdAbis = m_config.getAbis(m_config.adbToolPath(), m_devInfo.serialNumber); + m_serialNumber = devInfoLocal.serialNumber; + m_avdAbis = m_androidConfig.getAbis(m_androidConfig.adbToolPath(), m_serialNumber); } return !devInfoLocal.serialNumber.isEmpty(); } else { @@ -283,7 +261,7 @@ bool AndroidQmlPreviewWorker::ensureAvdIsRunning() } return false; } - m_avdAbis = m_config.getAbis(m_config.adbToolPath(), m_devInfo.serialNumber); + m_avdAbis = m_androidConfig.getAbis(m_androidConfig.adbToolPath(), m_serialNumber); return true; } @@ -291,7 +269,7 @@ bool AndroidQmlPreviewWorker::checkAndInstallPreviewApp() { const QStringList command {"pm", "list", "packages", apkInfo()->appId}; appendMessage(tr("Checking if %1 app is installed.").arg(apkInfo()->name), NormalMessageFormat); - const SdkToolResult res = runAdbShellCommand(m_devInfo.serialNumber, command); + const SdkToolResult res = runAdbShellCommand(command); if (!res.success()) { appendMessage(res.stdErr(), ErrorMessageFormat); return false; @@ -303,7 +281,7 @@ bool AndroidQmlPreviewWorker::checkAndInstallPreviewApp() ErrorMessageFormat); return false; } - const FilePath apkPath = viewerApkPath(m_avdAbis.first()); + const FilePath apkPath = designViewerApkPath(m_avdAbis.first()); if (!apkPath.exists()) { appendMessage(tr("Cannot install %1 app for %2 architecture. " "The appropriate APK was not found in resources folders."). @@ -313,32 +291,29 @@ bool AndroidQmlPreviewWorker::checkAndInstallPreviewApp() appendMessage(tr("Installing %1 APK.").arg(apkInfo()->name), NormalMessageFormat); - - const SdkToolResult res = runAdbCommand(m_devInfo.serialNumber, {"install", - apkPath.toString()}); - if (!res.success()) { + const SdkToolResult res = runAdbCommand({"install", apkPath.toString()}); + if (!res.success()) appendMessage(res.stdErr(), StdErrFormat); - - return false; - } } - return true; + + return res.success(); } -bool AndroidQmlPreviewWorker::prepareUpload(UploadInfo &transfer) +bool AndroidQmlPreviewWorker::preparePreviewArtefacts() { if (m_rc->project()->id() == QmlProjectManager::Constants::QML_PROJECT_ID) { const auto bs = m_rc->target()->buildSystem(); if (bs) { - transfer.uploadPackage = FilePath::fromString( + m_uploadInfo.uploadPackage = FilePath::fromString( bs->additionalData(QmlProjectManager::Constants::mainFilePath).toString()); - transfer.projectFolder = bs->projectDirectory(); + m_uploadInfo.projectFolder = bs->projectDirectory(); return true; } } else { const FilePaths allFiles = m_rc->project()->files(m_rc->project()->SourceFiles); const FilePaths filesToExport = Utils::filtered(allFiles,[](const FilePath &path) { - return path.suffix() == "qmlproject";}); + return path.suffix() == "qmlproject"; + }); if (filesToExport.size() > 1) { appendMessage(tr("Too many .qmlproject files in your project. Open directly the " @@ -348,10 +323,9 @@ bool AndroidQmlPreviewWorker::prepareUpload(UploadInfo &transfer) appendMessage(tr("No .qmlproject file found among project files."), ErrorMessageFormat); } else { const FilePath qmlprojectFile = filesToExport.first(); - transfer.uploadPackage = transfer. - projectFolder. - resolvePath(qmlprojectFile.fileName()); - transfer.projectFolder = qmlprojectFile.parentDir(); + m_uploadInfo.uploadPackage = m_uploadInfo.projectFolder.resolvePath( + qmlprojectFile.fileName()); + m_uploadInfo.projectFolder = qmlprojectFile.parentDir(); return true; } } @@ -366,16 +340,15 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder, const FilePath rccBinary = qtVersion->rccFilePath(); QtcProcess rccProcess; FilePath qrcPath = FilePath::fromString(basename) + ".qrc4viewer"; - const FilePath qmlrcPath = FilePath::fromString(QDir::tempPath() + "/" + basename + - packageSuffix); + const FilePath qmlrcPath = FilePath::fromString(QDir::tempPath()) / basename + packageSuffix; rccProcess.setWorkingDirectory(workFolder); const QStringList arguments[2] = {{"--project", "--output", qrcPath.fileName()}, {"--binary", "--output", qmlrcPath.path(), qrcPath.fileName()}}; - for (const auto &arguments : arguments) { - rccProcess.setCommand({rccBinary, arguments}); + for (const QStringList &args : arguments) { + rccProcess.setCommand({rccBinary, args}); rccProcess.start(); if (!rccProcess.waitForStarted()) { appendMessage(tr("Could not create file for %1 \"%2\""). @@ -420,71 +393,54 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder, return qmlrcPath; } -bool AndroidQmlPreviewWorker::uploadFiles(const UploadInfo &transfer) +bool AndroidQmlPreviewWorker::uploadPreviewArtefacts() { appendMessage(tr("Uploading files."), NormalMessageFormat); - - const FilePath qresPath = createQmlrcFile(FilePath::fromString(transfer.projectFolder.path()), - transfer.uploadPackage.baseName()); + const FilePath qresPath = createQmlrcFile(m_uploadInfo.projectFolder, + m_uploadInfo.uploadPackage.baseName()); if (!qresPath.exists()) return false; - runAdbShellCommand(m_devInfo.serialNumber, {"mkdir", "-p", apkInfo()->uploadDir}); - - const SdkToolResult res = runAdbCommand(m_devInfo.serialNumber, - {"push", qresPath.resolvePath(QString()).toString(), - apkInfo()->uploadDir}); + runAdbShellCommand({"mkdir", "-p", apkInfo()->uploadDir}); + const SdkToolResult res = runAdbCommand({"push", qresPath.resolvePath(QString()).toString(), + apkInfo()->uploadDir}); if (!res.success()) { appendMessage(res.stdOut(), ErrorMessageFormat); - if (res.stdOut().contains("Permission denied")) + if (res.stdOut().contains("Permission denied")) { appendMessage("'Permission denied' error detected. Try restarting your device " "and then running the preview.", NormalMessageFormat); + } } qresPath.removeFile(); return res.success(); } -bool AndroidQmlPreviewWorker::runPreviewApp(const UploadInfo &transfer) +bool AndroidQmlPreviewWorker::startPreviewApp() { stopPreviewApp(); appendMessage(tr("Starting %1.").arg(apkInfo()->name), NormalMessageFormat); const QDir destDir(apkInfo()->uploadDir); + const QString qmlrcPath = destDir.filePath(m_uploadInfo.uploadPackage.baseName() + + packageSuffix); const QStringList command{"am", "start", "-n", apkInfo()->activityId, - "-e", "extraappparams", - QString::fromLatin1( - destDir.filePath(transfer.uploadPackage.baseName() + packageSuffix). - toUtf8(). - toBase64())}; - const SdkToolResult res = runAdbShellCommand(m_devInfo.serialNumber, command); - if (!res.success()) { - appendMessage(res.stdErr(), ErrorMessageFormat); - return res.success(); - } - appendMessage(tr("%1 is running.").arg(apkInfo()->name), NormalMessageFormat); - m_viewerPid = pidofPreview(m_devInfo.serialNumber); - return true; + "-e", "extraappparams", QLatin1String(qmlrcPath.toUtf8().toBase64())}; + const SdkToolResult result = runAdbShellCommand(command); + if (result.success()) + appendMessage(tr("%1 is running.").arg(apkInfo()->name), NormalMessageFormat); + else + appendMessage(result.stdErr(), ErrorMessageFormat); + + return result.success(); } bool AndroidQmlPreviewWorker::stopPreviewApp() { const QStringList command{"am", "force-stop", apkInfo()->appId}; - const SdkToolResult res = runAdbShellCommand(m_devInfo.serialNumber, command); - if (!res.success()) { + const SdkToolResult res = runAdbShellCommand(command); + if (!res.success()) appendMessage(res.stdErr(), ErrorMessageFormat); - return res.success(); - } - return true; -} - -void AndroidQmlPreviewWorker::appendLogLines(const QStringList & lines) -{ - for (const QString& line : lines) { - const int charsToSkip = apkInfo()->name.length() + 2; // strlen(": ") == 2 - const QString formatted = line.mid(line.indexOf(apkInfo()->name) + charsToSkip); - // TODO: See AndroidRunnerWorker::logcatProcess() - filtering for logs to decide format. - appendMessage(formatted, StdOutFormat); - } + return res.success(); } } // namespace Internal diff --git a/src/plugins/android/androidqmlpreviewworker.h b/src/plugins/android/androidqmlpreviewworker.h index e014107df54..c837570e01f 100644 --- a/src/plugins/android/androidqmlpreviewworker.h +++ b/src/plugins/android/androidqmlpreviewworker.h @@ -25,9 +25,12 @@ #pragma once #include "androidconfigurations.h" + #include <projectexplorer/runcontrol.h> #include <utils/environment.h> +#include <QFutureWatcher> + namespace Android { class SdkToolResult; @@ -45,6 +48,10 @@ class AndroidQmlPreviewWorker : public ProjectExplorer::RunWorker Q_OBJECT public: AndroidQmlPreviewWorker(ProjectExplorer::RunControl *runControl); + ~AndroidQmlPreviewWorker(); + +signals: + void previewPidChanged(); private: void start() override; @@ -52,22 +59,33 @@ private: bool ensureAvdIsRunning(); bool checkAndInstallPreviewApp(); - bool prepareUpload(UploadInfo &transfer); - bool uploadFiles(const UploadInfo &transfer); + bool preparePreviewArtefacts(); + bool uploadPreviewArtefacts(); - bool runPreviewApp(const UploadInfo &transfer); - bool stopPreviewApp(); + SdkToolResult runAdbCommand(const QStringList &arguments) const; + SdkToolResult runAdbShellCommand(const QStringList &arguments) const; + int pidofPreview() const; + bool isPreviewRunning(int lastKnownPid = -1) const; + void startPidWatcher(); void startLogcat(); - void appendLogLines(const QStringList &lines); + void filterLogcatAndAppendMessage(const QString &stdOut); + + bool startPreviewApp(); + bool stopPreviewApp(); + Utils::FilePath designViewerApkPath(const QString &abi) const; Utils::FilePath createQmlrcFile(const Utils::FilePath &workFolder, const QString &basename); ProjectExplorer::RunControl *m_rc = nullptr; - AndroidConfig m_config; - AndroidDeviceInfo m_devInfo; + AndroidConfig m_androidConfig; + QString m_serialNumber; QStringList m_avdAbis; int m_viewerPid = -1; + QFutureWatcher<void> m_pidFutureWatcher; + Utils::QtcProcess m_logcatProcess; + QString m_logcatStartTimeStamp; + UploadInfo m_uploadInfo; }; } // namespace Internal |