Skip to content

Commit

Permalink
Keep main Qt event loop running when Android app is in background
Browse files Browse the repository at this point in the history
* Process Syncthing events and possibly update Android notifications
* React to network connection changes (metered vs. not metered) while the
  app is in background

This might not be ideal:

* According to the documentation it'll lead to crashes when the app tries
  to draw while in background. I didn't run into any problems after a brief
  test so far, though.
* UI code will run unneccassarily while the app is in the background. The
  code is already unassinging the main thread affinity of models to reduce
  this. It is unfortunately not possible to move the QML engine due to its
  bindings (`QObject::moveToThread: Can not move objects that contain
  bindings or are used in bindings to a new thread.`).
  • Loading branch information
Martchus committed Jan 9, 2025
1 parent 7e73f11 commit 8c2ad19
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 2 deletions.
12 changes: 10 additions & 2 deletions tray/gui/quick/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ App::App(bool insecure, QObject *parent)
, m_sortFilterDevModel(&m_devModel)
, m_changesModel(m_connection)
, m_faUrlBase(QStringLiteral("image://fa/"))
, m_uiObjects({&m_dirModel, &m_sortFilterDirModel, &m_devModel, &m_sortFilterDevModel, &m_changesModel})
, m_iconSize(16)
, m_tabIndex(-1)
, m_importExportStatus(ImportExportStatus::None)
Expand Down Expand Up @@ -698,14 +699,21 @@ void App::handleNewErrors(const std::vector<Data::SyncthingError> &errors)
void App::handleStateChanged(Qt::ApplicationState state)
{
if (m_isGuiLoaded && ((state == Qt::ApplicationSuspended) || (state & Qt::ApplicationHidden))) {
qDebug() << "App considered suspended/hidden, reduce polling, stopping UI if requested";
qDebug() << "App considered suspended/hidden, reducing polling, stopping UI processing";
setCurrentControls(false);
for (auto *const uiObject : m_uiObjects) {
uiObject->moveToThread(nullptr);
}
if (m_unloadGuiWhenHidden) {
unloadMain();
}
} else if (state & Qt::ApplicationActive) {
qDebug() << "App considered active, ensuring UI is loaded";
qDebug() << "App considered active, continuing polling, resuming UI processing";
setCurrentControls(true);
auto *const uiThread = thread();
for (auto *const uiObject : m_uiObjects) {
uiObject->moveToThread(uiThread);
}
if (!m_isGuiLoaded) {
deletePipelineCache();
loadMain();
Expand Down
2 changes: 2 additions & 0 deletions tray/gui/quick/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <QJniObject>
#endif

#include <array>
#include <optional>

QT_FORWARD_DECLARE_CLASS(QTextDocument)
Expand Down Expand Up @@ -328,6 +329,7 @@ private Q_SLOTS:
QJsonObject m_settings;
QString m_faUrlBase;
std::optional<QString> m_status;
std::array<QObject *, 5> m_uiObjects;
QString m_log;
int m_iconSize;
int m_tabIndex;
Expand Down
4 changes: 4 additions & 0 deletions tray/resources/AndroidManifest.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
<meta-data
android:name="android.app.splash_screen_drawable"
android:resource="@drawable/splash" />
<!-- keep main Qt event loop running when app is in background -->
<meta-data
android:name="android.app.background_running"
android:value="true" />
</activity>
<service android:name=".SyncthingService" android:foregroundServiceType="specialUse" android:exported="true">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Synchronizes files continuously in the background."/>
Expand Down

0 comments on commit 8c2ad19

Please sign in to comment.