diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8dceda2bdaa4146b1d2e8607ec9086f15c72ecf5..f1ccd5302c5525ad6dbe1b0a055968c012036b28 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,7 +69,9 @@ set(COMMON_SOURCES
     ${SRC_DIR}/dbuserrorhandler.cpp
     ${SRC_DIR}/xrectsel.c
     ${SRC_DIR}/moderatorlistmodel.cpp
-    ${SRC_DIR}/screensaver.cpp)
+    ${SRC_DIR}/screensaver.cpp
+    ${SRC_DIR}/systemtray.cpp
+    ${SRC_DIR}/appsettingsmanager.cpp)
 
 set(COMMON_HEADERS
     ${SRC_DIR}/avatarimageprovider.h
@@ -82,8 +84,6 @@ set(COMMON_HEADERS
     ${SRC_DIR}/accountlistmodel.h
     ${SRC_DIR}/runguard.h
     ${SRC_DIR}/lrcinstance.h
-    ${SRC_DIR}/globalsystemtray.h
-    ${SRC_DIR}/appsettingsmanager.h
     ${SRC_DIR}/webchathelpers.h
     ${SRC_DIR}/rendermanager.h
     ${SRC_DIR}/connectivitymonitor.h
@@ -122,7 +122,9 @@ set(COMMON_HEADERS
     ${SRC_DIR}/dbuserrorhandler.h
     ${SRC_DIR}/xrectsel.h
     ${SRC_DIR}/moderatorlistmodel.h
-    ${SRC_DIR}/screensaver.h)
+    ${SRC_DIR}/screensaver.h
+    ${SRC_DIR}/systemtray.h
+    ${SRC_DIR}/appsettingsmanager.h)
 
 find_package(PkgConfig REQUIRED)
 pkg_check_modules(LIBNM libnm)
diff --git a/README.md b/README.md
index bd5b5ea503356453ef9a29f9ef1ce057386fc6e4..c6d040d4a9fac3c632c5ff3cb59bbe556cfbffaa 100644
--- a/README.md
+++ b/README.md
@@ -92,7 +92,7 @@ Variables `QT5_VER` and `QT5_PATH` are used to specify version and path for a cu
 
 If lrc library is installed in a custom directory you can set its path with the variable LRC. Additionally you can specify built library location with `LRCLIB` (otherwise it will seach inside LRC with the suffixes `/lib`, `/build` and `/build-local`).
 
-After the build has finished, you are finally ready to launch jami-qt in your build directory. 
+After the build has finished, you are finally ready to launch jami-qt in your build directory.
 
 If you want to install it to the path provided by `CMAKE_INSTALL_PREFIX` you can run:
 
@@ -162,7 +162,7 @@ Only 64-bit MSVC build can be compiled.
 > By default: ```toolset=v141```, ```sdk=10.0.16299.0```,  ```qtver=5.9.4``` <br>
 > For example:
 ```sh
-    python make-ring.py --install --toolset v142 --sdk 10.0.18362.0 --qtver 5.12.0
+    python make-ring.py --install --toolset v142 --sdk 10.0.18362.0 --qtver 5.14.0
 ```
 
 ### Build Module individually
diff --git a/jami-qt.pro b/jami-qt.pro
index 532e1d10cf470aea6efd18cc7199df1c08b53f57..6ed6cdf96f6c0ce58f50a8fdafd146557ee98386 100644
--- a/jami-qt.pro
+++ b/jami-qt.pro
@@ -176,8 +176,6 @@ HEADERS += \
         src/accountlistmodel.h \
         src/runguard.h \
         src/lrcinstance.h \
-        src/globalsystemtray.h \
-        src/appsettingsmanager.h \
         src/webchathelpers.h \
         src/rendermanager.h \
         src/connectivitymonitor.h \
@@ -210,7 +208,9 @@ HEADERS += \
         src/audiomanagerlistmodel.h \
         src/qmlregister.h \
         src/qtutils.h \
-        src/utilsadapter.h
+        src/utilsadapter.h \
+        src/systemtray.h \
+        src/appsettingsmanager.h
 
 SOURCES += \
         src/bannedlistmodel.cpp \
@@ -250,7 +250,9 @@ SOURCES += \
         src/videoformatresolutionmodel.cpp \
         src/audiomanagerlistmodel.cpp \
         src/qmlregister.cpp \
-        src/utilsadapter.cpp
+        src/utilsadapter.cpp \
+        src/systemtray.cpp \
+        src/appsettingsmanager.cpp
 
 RESOURCES += \
         resources.qrc \
diff --git a/src/accountadapter.cpp b/src/accountadapter.cpp
index 62a3801659da7f8cb392439552a9de4576a38ef2..eef1d08877f33c3ecf7670ac163a4817a4900c1a 100644
--- a/src/accountadapter.cpp
+++ b/src/accountadapter.cpp
@@ -23,12 +23,16 @@
 
 #include "accountadapter.h"
 
+#include "appsettingsmanager.h"
 #include "qtutils.h"
 
 #include <QtConcurrent/QtConcurrent>
 
-AccountAdapter::AccountAdapter(LRCInstance* instance, QObject* parent)
+AccountAdapter::AccountAdapter(AppSettingsManager* settingsManager,
+                               LRCInstance* instance,
+                               QObject* parent)
     : QmlAdapterBase(instance, parent)
+    , settingsManager_(settingsManager)
 {}
 
 void
@@ -100,7 +104,7 @@ AccountAdapter::createJamiAccount(QString registeredName,
             lrcInstance_->accountModel().setAccountConfig(accountId, confProps);
 
             auto showBackup = isCreating
-                              && !AppSettingsManager::getValue(Settings::Key::NeverShowMeAgain)
+                              && !settingsManager_->getValue(Settings::Key::NeverShowMeAgain)
                                       .toBool();
             if (!registeredName.isEmpty()) {
                 QObject::disconnect(registeredNameSavedConnection_);
diff --git a/src/accountadapter.h b/src/accountadapter.h
index 8abaf3b161bad7f7105c2095617681d6f6494ece..3739b369aedc6be712c2b76556ff1f559d9138c2 100644
--- a/src/accountadapter.h
+++ b/src/accountadapter.h
@@ -27,6 +27,8 @@
 #include "lrcinstance.h"
 #include "utils.h"
 
+class AppSettingsManager;
+
 class AccountAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
@@ -50,7 +52,9 @@ signals:
     void accountListSizeChanged();
 
 public:
-    explicit AccountAdapter(LRCInstance* instance, QObject* parent = nullptr);
+    explicit AccountAdapter(AppSettingsManager* settingsManager,
+                            LRCInstance* instance,
+                            QObject* parent = nullptr);
     ~AccountAdapter() = default;
 
 protected:
@@ -132,5 +136,7 @@ private:
     QMetaObject::Connection addedToConferenceConnection_;
     QMetaObject::Connection contactUnbannedConnection_;
     QMetaObject::Connection registeredNameSavedConnection_;
+
+    AppSettingsManager* settingsManager_;
 };
 Q_DECLARE_METATYPE(AccountAdapter*)
diff --git a/src/appsettingsmanager.cpp b/src/appsettingsmanager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2be2735aedb6c8a6d4a33df64178146c7505610e
--- /dev/null
+++ b/src/appsettingsmanager.cpp
@@ -0,0 +1,50 @@
+/*!
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * \file appsettingsmanager.cpp
+ */
+
+#include "appsettingsmanager.h"
+
+AppSettingsManager::AppSettingsManager(QObject* parent)
+    : QObject(parent)
+    , settings_(new QSettings("jami.net", "Jami", this))
+{
+    for (int i = 0; i < static_cast<int>(Settings::Key::COUNT__); ++i) {
+        auto key = static_cast<Settings::Key>(i);
+        if (!settings_->contains(Settings::toString(key)))
+            setValue(key, Settings::defaultValue(key));
+    }
+}
+
+QVariant
+AppSettingsManager::getValue(const Settings::Key key)
+{
+    auto value = settings_->value(Settings::toString(key), Settings::defaultValue(key));
+
+    if (QString(value.typeName()) == "QString"
+        && (value.toString() == "false" || value.toString() == "true"))
+        return value.toBool();
+
+    return value;
+}
+
+void
+AppSettingsManager::setValue(const Settings::Key key, const QVariant& value)
+{
+    settings_->setValue(Settings::toString(key), value);
+}
diff --git a/src/appsettingsmanager.h b/src/appsettingsmanager.h
index 53a1fbb9d3be52490ae3a0ce390acbe98904570e..7d7764b12a86a36b38d65e573505e3c52aefa4f1 100644
--- a/src/appsettingsmanager.h
+++ b/src/appsettingsmanager.h
@@ -1,5 +1,5 @@
 /*!
- * Copyright (C) 2020 by Savoir-faire Linux
+ * Copyright (C) 2020-2021 by Savoir-faire Linux
  * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -84,51 +84,16 @@ private:
 Q_DECLARE_METATYPE(Settings::Key)
 // clang-format on
 
-/*
- * A singleton object to manage settings access.
- */
 class AppSettingsManager : public QObject
 {
     Q_OBJECT
 public:
-    virtual ~AppSettingsManager() = default;
-
-    static AppSettingsManager& instance()
-    {
-        static AppSettingsManager* instance_ = new AppSettingsManager(nullptr);
-        return *instance_;
-    }
+    explicit AppSettingsManager(QObject* parent = nullptr);
+    ~AppSettingsManager() = default;
 
-    static QVariant getValue(const Settings::Key key)
-    {
-        auto settings = instance().settings_;
-        auto value = settings->value(Settings::toString(key), Settings::defaultValue(key));
-
-        if (QString(value.typeName()) == "QString"
-            && (value.toString() == "false" || value.toString() == "true"))
-            return value.toBool();
-
-        return value;
-    }
-
-    static void setValue(const Settings::Key key, const QVariant& value)
-    {
-        instance().settings_->setValue(Settings::toString(key), value);
-    }
-
-    static void initValues()
-    {
-        for (int i = 0; i < static_cast<int>(Settings::Key::COUNT__); ++i) {
-            auto key = static_cast<Settings::Key>(i);
-            if (!instance().settings_->contains(Settings::toString(key)))
-                setValue(key, Settings::defaultValue(key));
-        }
-    }
+    QVariant getValue(const Settings::Key key);
+    void setValue(const Settings::Key key, const QVariant& value);
 
 private:
-    explicit AppSettingsManager(QObject*)
-        : settings_(new QSettings("jami.net", "Jami", this))
-    {}
-
     QSettings* settings_;
 };
diff --git a/src/calladapter.cpp b/src/calladapter.cpp
index ba9030bbd2d870085d5468982161aaf63fe41f4e..67236bc54d0d56982242f34909b217a3430c2963 100644
--- a/src/calladapter.cpp
+++ b/src/calladapter.cpp
@@ -23,13 +23,16 @@
  */
 
 #include "calladapter.h"
+
+#include "systemtray.h"
 #include "utils.h"
 
 #include <QApplication>
 
-CallAdapter::CallAdapter(LRCInstance* instance, QObject* parent)
+CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent)
     : QmlAdapterBase(instance, parent)
     , oneSecondTimer_(new QTimer(this))
+    , systemTray_(systemTray)
 {
     accountId_ = lrcInstance_->getCurrAccId();
     connectCallModel(accountId_);
@@ -335,7 +338,7 @@ CallAdapter::showNotification(const QString& accountId, const QString& convUid)
         emit callSetupMainViewRequired(convInfo.accountId, convInfo.uid);
     };
     emit lrcInstance_->updateSmartList();
-    Utils::showNotification(tr("is calling you"), from, accountId, convUid, onClicked);
+    systemTray_->showNotification(tr("is calling you"), from, onClicked);
 }
 
 void
diff --git a/src/calladapter.h b/src/calladapter.h
index 3a057971bbf4881ae039e60dcaf64019ca9853cb..0a3ada3605dcda274780eb10b2e6943d9d4c7ee7 100644
--- a/src/calladapter.h
+++ b/src/calladapter.h
@@ -28,6 +28,8 @@
 #include <QVariant>
 #include <QSystemTrayIcon>
 
+class SystemTray;
+
 class CallAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
@@ -36,7 +38,7 @@ public:
     enum MuteStates { UNMUTED, LOCAL_MUTED, MODERATOR_MUTED, BOTH_MUTED };
     Q_ENUM(MuteStates)
 
-    explicit CallAdapter(LRCInstance* instance, QObject* parent = nullptr);
+    explicit CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent = nullptr);
     ~CallAdapter() = default;
 
 protected:
@@ -124,4 +126,6 @@ private:
     ScreenSaver screenSaver;
 
     void preventScreenSaver(bool state);
+
+    SystemTray* systemTray_;
 };
diff --git a/src/contactadapter.h b/src/contactadapter.h
index 54c02a7c2d6e13b6e44a7bc1c992613b42d44fcf..5b56697ad892af80d42eb5572215aa3741981aaf 100644
--- a/src/contactadapter.h
+++ b/src/contactadapter.h
@@ -56,11 +56,8 @@ public:
     virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
     {
         // Accept all contacts in conversation list filtered with account type, except those in a call.
-
         auto index = sourceModel()->index(source_row, 0, source_parent);
-        if (filterPredicate_) {
-            return filterPredicate_(index, filterRegExp());
-        }
+        return filterPredicate_ ? filterPredicate_(index, filterRegExp()) : false;
     }
 
 private:
diff --git a/src/conversationsadapter.cpp b/src/conversationsadapter.cpp
index 961f4bb187d309df042e6c2f46a865d34049b8d5..7e1b5a950621a68980d371da33c7265daab3d170 100644
--- a/src/conversationsadapter.cpp
+++ b/src/conversationsadapter.cpp
@@ -25,11 +25,15 @@
 
 #include "utils.h"
 #include "qtutils.h"
+#include "systemtray.h"
 
 #include <QApplication>
 
-ConversationsAdapter::ConversationsAdapter(LRCInstance* instance, QObject* parent)
+ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray,
+                                           LRCInstance* instance,
+                                           QObject* parent)
     : QmlAdapterBase(instance, parent)
+    , systemTray_(systemTray)
 {
     connect(this, &ConversationsAdapter::currentTypeFilterChanged, [this]() {
         lrcInstance_->getCurrentConversationModel()->setFilter(currentTypeFilter_);
@@ -163,7 +167,7 @@ ConversationsAdapter::onNewUnreadInteraction(const QString& accountId,
             }
         };
 
-        Utils::showNotification(interaction.body, from, accountId, convUid, onClicked);
+        systemTray_->showNotification(interaction.body, from, onClicked);
         return;
     }
 }
diff --git a/src/conversationsadapter.h b/src/conversationsadapter.h
index 6961229b7c77fef69c56682d24baf0040a13aebd..fc3a17f8a810107c77beef4d17da3c7a4fa2f929 100644
--- a/src/conversationsadapter.h
+++ b/src/conversationsadapter.h
@@ -25,6 +25,8 @@
 #include <QObject>
 #include <QString>
 
+class SystemTray;
+
 class ConversationsAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
@@ -32,7 +34,9 @@ class ConversationsAdapter final : public QmlAdapterBase
     Q_PROPERTY(lrc::api::profile::Type currentTypeFilter MEMBER currentTypeFilter_ NOTIFY
                    currentTypeFilterChanged)
 public:
-    explicit ConversationsAdapter(LRCInstance* instance, QObject* parent = nullptr);
+    explicit ConversationsAdapter(SystemTray* systemTray,
+                                  LRCInstance* instance,
+                                  QObject* parent = nullptr);
     ~ConversationsAdapter() = default;
 
 protected:
@@ -86,4 +90,6 @@ private:
     QMetaObject::Connection interactionRemovedConnection_;
     QMetaObject::Connection searchStatusChangedConnection_;
     QMetaObject::Connection searchResultUpdatedConnection_;
+
+    SystemTray* systemTray_;
 };
diff --git a/src/lrcinstance.h b/src/lrcinstance.h
index 0f7efedeb325bc6d3726174fe4a22aadeee18814..339e7e15b6835ade3c5214b6d554005211c36cd0 100644
--- a/src/lrcinstance.h
+++ b/src/lrcinstance.h
@@ -26,7 +26,6 @@
 
 #include "updatemanager.h"
 #include "rendermanager.h"
-#include "appsettingsmanager.h"
 #include "utils.h"
 
 #include "api/account.h"
diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp
index d734dedb84834fbfa97ca045dac9db58b33a75a1..33408c8de2128f15bfc8fc9c38f58c31ff21df4e 100644
--- a/src/mainapplication.cpp
+++ b/src/mainapplication.cpp
@@ -21,15 +21,25 @@
 
 #include "mainapplication.h"
 
+#include "qmlregister.h"
 #include "appsettingsmanager.h"
 #include "connectivitymonitor.h"
-#include "globalsystemtray.h"
+#include "systemtray.h"
 #include "namedirectory.h"
-#include "qmlregister.h"
 #include "qrimageprovider.h"
 #include "tintedbuttonimageprovider.h"
 #include "avatarimageprovider.h"
 
+#include "accountadapter.h"
+#include "avadapter.h"
+#include "calladapter.h"
+#include "contactadapter.h"
+#include "pluginadapter.h"
+#include "messagesadapter.h"
+#include "settingsadapter.h"
+#include "utilsadapter.h"
+#include "conversationsadapter.h"
+
 #include <QAction>
 #include <QCommandLineParser>
 #include <QCoreApplication>
@@ -146,6 +156,8 @@ MainApplication::MainApplication(int& argc, char** argv)
     : QApplication(argc, argv)
     , engine_(new QQmlApplicationEngine())
     , connectivityMonitor_(new ConnectivityMonitor(this))
+    , settingsManager_(new AppSettingsManager(this))
+    , systemTray_(new SystemTray(settingsManager_, this))
 {
     QObject::connect(this, &QApplication::aboutToQuit, [this] { cleanup(); });
 }
@@ -233,9 +245,11 @@ MainApplication::init()
         vsConsoleDebug();
     }
 
-    initSettings();
+    auto downloadPath = settingsManager_->getValue(Settings::Key::DownloadPath);
+    lrcInstance_->dataTransferModel().downloadDirectory = downloadPath.toString() + "/";
+
+    initQmlLayer();
     initSystray();
-    initQmlEngine();
 
     return true;
 }
@@ -394,9 +408,37 @@ MainApplication::setApplicationFont()
 }
 
 void
-MainApplication::initQmlEngine()
+MainApplication::initQmlLayer()
 {
-    registerTypes(lrcInstance_.get());
+    // setup the adapters (their lifetimes are that of MainApplication)
+    auto callAdapter = new CallAdapter(systemTray_, lrcInstance_.data(), this);
+    auto messagesAdapter = new MessagesAdapter(settingsManager_, lrcInstance_.data(), this);
+    auto conversationsAdapter = new ConversationsAdapter(systemTray_, lrcInstance_.data(), this);
+    auto avAdapter = new AvAdapter(lrcInstance_.data(), this);
+    auto contactAdapter = new ContactAdapter(lrcInstance_.data(), this);
+    auto accountAdapter = new AccountAdapter(settingsManager_, lrcInstance_.data(), this);
+    auto utilsAdapter = new UtilsAdapter(systemTray_, lrcInstance_.data(), this);
+    auto settingsAdapter = new SettingsAdapter(settingsManager_, lrcInstance_.data(), this);
+    auto pluginAdapter = new PluginAdapter(lrcInstance_.data(), this);
+
+    // qml adapter registration
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, callAdapter, "CallAdapter");
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, messagesAdapter, "MessagesAdapter");
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, conversationsAdapter, "ConversationsAdapter");
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, avAdapter, "AvAdapter");
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, contactAdapter, "ContactAdapter");
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, accountAdapter, "AccountAdapter");
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, utilsAdapter, "UtilsAdapter");
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, settingsAdapter, "SettingsAdapter");
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, pluginAdapter, "PluginAdapter");
+
+    // TODO: remove these
+    QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, AVModel, &lrcInstance_->avModel())
+    QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, PluginModel, &lrcInstance_->pluginModel())
+    QML_REGISTERSINGLETONTYPE_CUSTOM(NS_HELPERS, UpdateManager, lrcInstance_->getUpdateManager())
+
+    // register other types that don't require injection(e.g. uncreatables, c++/qml singletons)
+    Utils::registerTypes();
 
     engine_->addImageProvider(QLatin1String("qrImage"), new QrImageProvider(lrcInstance_.get()));
     engine_->addImageProvider(QLatin1String("tintedPixmap"),
@@ -416,19 +458,10 @@ MainApplication::initQmlEngine()
     engine_->load(QUrl(QStringLiteral("qrc:/src/MainApplicationWindow.qml")));
 }
 
-void
-MainApplication::initSettings()
-{
-    AppSettingsManager::instance().initValues();
-    auto downloadPath = AppSettingsManager::instance().getValue(Settings::Key::DownloadPath);
-    lrcInstance_->dataTransferModel().downloadDirectory = downloadPath.toString() + "/";
-}
-
 void
 MainApplication::initSystray()
 {
-    GlobalSystemTray& sysIcon = GlobalSystemTray::instance();
-    sysIcon.setIcon(QIcon(":images/jami.png"));
+    systemTray_->setIcon(QIcon(":images/jami.png"));
 
     QMenu* systrayMenu = new QMenu();
 
@@ -437,20 +470,21 @@ MainApplication::initSystray()
         engine_->quit();
         cleanup();
     });
-    connect(&sysIcon, &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason) {
-        if (reason != QSystemTrayIcon::ActivationReason::Context)
-            restoreApp();
-    });
+    connect(systemTray_,
+            &QSystemTrayIcon::activated,
+            [this](QSystemTrayIcon::ActivationReason reason) {
+                if (reason != QSystemTrayIcon::ActivationReason::Context)
+                    restoreApp();
+            });
 
     systrayMenu->addAction(exitAction);
-    sysIcon.setContextMenu(systrayMenu);
-    sysIcon.show();
+    systemTray_->setContextMenu(systrayMenu);
+    systemTray_->show();
 }
 
 void
 MainApplication::cleanup()
 {
-    GlobalSystemTray::instance().hide();
 #ifdef Q_OS_WIN
     FreeConsole();
 #endif
diff --git a/src/mainapplication.h b/src/mainapplication.h
index f2f6273c68de6fa5cf7776859c609681bb87d3da..775b364b7e04c8f0031aed2c8679d2b569539ae8 100644
--- a/src/mainapplication.h
+++ b/src/mainapplication.h
@@ -32,6 +32,8 @@
 #include <memory>
 
 class ConnectivityMonitor;
+class AppSettingsManager;
+class SystemTray;
 
 // Provides information about the screen the app is displayed on
 class ScreenInfo : public QObject
@@ -81,16 +83,19 @@ private:
     void initLrc(const QString& downloadUrl, ConnectivityMonitor* cm);
     const QVariantMap parseArguments();
     void setApplicationFont();
-    void initQmlEngine();
-    void initSettings();
+    void initQmlLayer();
     void initSystray();
     void cleanup();
 
 private:
     QScopedPointer<QFile> debugFile_;
     QScopedPointer<QQmlApplicationEngine> engine_;
+
     QScopedPointer<LRCInstance> lrcInstance_;
-    ConnectivityMonitor* connectivityMonitor_ {nullptr};
+
+    ConnectivityMonitor* connectivityMonitor_;
+    AppSettingsManager* settingsManager_;
+    SystemTray* systemTray_;
 
     ScreenInfo screenInfo_;
 };
diff --git a/src/messagesadapter.cpp b/src/messagesadapter.cpp
index 82947582832dc7b34a27eddd6b2ef5389c8591b4..ec37d94aca61bed5c027292fa69c7aa8c8dc5cf8 100644
--- a/src/messagesadapter.cpp
+++ b/src/messagesadapter.cpp
@@ -23,6 +23,7 @@
 
 #include "messagesadapter.h"
 
+#include "appsettingsmanager.h"
 #include "qtutils.h"
 #include "utils.h"
 #include "webchathelpers.h"
@@ -35,8 +36,11 @@
 #include <QList>
 #include <QUrl>
 
-MessagesAdapter::MessagesAdapter(LRCInstance* instance, QObject* parent)
+MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager,
+                                 LRCInstance* instance,
+                                 QObject* parent)
     : QmlAdapterBase(instance, parent)
+    , settingsManager_(settingsManager)
 {}
 
 void
@@ -94,7 +98,7 @@ MessagesAdapter::setupChatView(const QString& convUid)
     connect(lrcInstance_->getCurrentConversationModel(),
             &ConversationModel::composingStatusChanged,
             [this](const QString& convUid, const QString& contactUri, bool isComposing) {
-                if (!AppSettingsManager::getValue(Settings::Key::EnableTypingIndicator).toBool()) {
+                if (!settingsManager_->getValue(Settings::Key::EnableTypingIndicator).toBool()) {
                     return;
                 }
                 contactIsComposing(convUid, contactUri, isComposing);
@@ -438,7 +442,7 @@ MessagesAdapter::pasteKeyDetected()
 void
 MessagesAdapter::onComposing(bool isComposing)
 {
-    if (!AppSettingsManager::getValue(Settings::Key::EnableTypingIndicator).toBool()) {
+    if (!settingsManager_->getValue(Settings::Key::EnableTypingIndicator).toBool()) {
         return;
     }
     lrcInstance_->getCurrentConversationModel()->setIsComposing(lrcInstance_->getCurrentConvUid(),
@@ -550,8 +554,7 @@ void
 MessagesAdapter::setDisplayLinks()
 {
     QString s = QString::fromLatin1("setDisplayLinks(%1);")
-                    .arg(
-                        AppSettingsManager::getValue(Settings::Key::DisplayImagesChatview).toBool());
+                    .arg(settingsManager_->getValue(Settings::Key::DisplayImagesChatview).toBool());
     QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s));
 }
 
diff --git a/src/messagesadapter.h b/src/messagesadapter.h
index cf9f3c47f6263a1c2ab4248637a86308e66eae63..3d12d6c063f5938e8cdefd366449c0d629ef96ca 100644
--- a/src/messagesadapter.h
+++ b/src/messagesadapter.h
@@ -25,13 +25,17 @@
 #include <QObject>
 #include <QString>
 
+class AppSettingsManager;
+
 class MessagesAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
     Q_PROPERTY(QVariantMap chatviewTranslatedStrings MEMBER chatviewTranslatedStrings_ CONSTANT)
 
 public:
-    explicit MessagesAdapter(LRCInstance* instance, QObject* parent = nullptr);
+    explicit MessagesAdapter(AppSettingsManager* settingsManager,
+                             LRCInstance* instance,
+                             QObject* parent = nullptr);
     ~MessagesAdapter() = default;
 
 protected:
@@ -115,4 +119,6 @@ private:
     QMetaObject::Connection newInteractionConnection_;
     QMetaObject::Connection interactionStatusUpdatedConnection_;
     QMetaObject::Connection interactionRemovedConnection_;
+
+    AppSettingsManager* settingsManager_;
 };
diff --git a/src/qmlregister.cpp b/src/qmlregister.cpp
index 71b0da7856466beefb14084f289d03bd8ede5ede..78adb0ce6433c8715d8db06242838572c7c93795 100644
--- a/src/qmlregister.cpp
+++ b/src/qmlregister.cpp
@@ -18,28 +18,22 @@
 
 #include "qmlregister.h"
 
-#include "accountadapter.h"
 #include "accountlistmodel.h"
 #include "accountstomigratelistmodel.h"
 #include "mediacodeclistmodel.h"
 #include "audiodevicemodel.h"
 #include "audiomanagerlistmodel.h"
-#include "avadapter.h"
 #include "bannedlistmodel.h"
 #include "moderatorlistmodel.h"
-#include "calladapter.h"
-#include "contactadapter.h"
-#include "conversationsadapter.h"
 #include "deviceitemlistmodel.h"
+#include "smartlistmodel.h"
+
+#include "appsettingsmanager.h"
 #include "distantrenderer.h"
-#include "pluginadapter.h"
-#include "messagesadapter.h"
 #include "namedirectory.h"
 #include "updatemanager.h"
 #include "pluginlistpreferencemodel.h"
 #include "previewrenderer.h"
-#include "settingsadapter.h"
-#include "utilsadapter.h"
 #include "version.h"
 #include "videoformatfpsmodel.h"
 #include "videoformatresolutionmodel.h"
@@ -49,116 +43,91 @@
 #include <QQmlEngine>
 
 // clang-format off
-#define QML_REGISTERSINGLETONTYPE_ADAPTER(N, T, MAJ, MIN) \
-    qmlRegisterSingletonType<T>(N, MAJ, MIN, #T, \
-                                [instance](QQmlEngine* e, QJSEngine* se) -> QObject* { \
-                                    Q_UNUSED(e); Q_UNUSED(se); \
-                                    T* obj = new T(nullptr, instance); return obj; \
-                                });
-
-#define QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(T, MAJ, MIN) \
-    qmlRegisterSingletonType<T>("net.jami.Models", MAJ, MIN, #T, \
+// TODO: remove this
+#define QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(T) \
+    qmlRegisterSingletonType<T>(NS_MODELS, VER_MAJ, VER_MIN, #T, \
                                 [](QQmlEngine* e, QJSEngine* se) -> QObject* { \
                                     Q_UNUSED(e); Q_UNUSED(se); \
                                     return &(T::instance()); \
                                 });
 
-#define QML_REGISTERSINGLETONTYPE_CUSTOM(N, T, MAJ, MIN, P) \
-    qmlRegisterSingletonType<T>(N, MAJ, MIN, #T, \
-                                [instance](QQmlEngine* e, QJSEngine* se) -> QObject* { \
-                                    Q_UNUSED(e); Q_UNUSED(se); \
-                                    return P; \
-                                });
+#define QML_REGISTERSINGLETONTYPE_URL(NS, URL, T) \
+    qmlRegisterSingletonType(QUrl(QStringLiteral(URL)), NS, VER_MAJ, VER_MIN, #T);
 
-#define QML_REGISTERSINGLETONTYPE_URL(N, URL, T, MAJ, MIN) \
-    qmlRegisterSingletonType(QUrl(QStringLiteral(URL)), N, MAJ, MIN, #T);
+#define QML_REGISTERTYPE(NS, T) qmlRegisterType<T>(NS, VER_MAJ, VER_MIN, #T);
 
-#define QML_REGISTERTYPE(N, T, MAJ, MIN) qmlRegisterType<T>(N, MAJ, MIN, #T);
+#define QML_REGISTERNAMESPACE(T, NAME) \
+    qmlRegisterUncreatableMetaObject(T, NS_MODELS, VER_MAJ, VER_MIN, NAME, "")
 
-#define QML_REGISTERNAMESPACE(T, NAME, MAJ, MIN) \
-    qmlRegisterUncreatableMetaObject(T, "net.jami.Models", MAJ, MIN, NAME, "")
+#define QML_REGISTERUNCREATABLE(N, T) \
+    qmlRegisterUncreatableType<T>(N, VER_MAJ, VER_MIN, #T, "Don't try to add to a qml definition of " #T);
 
-#define QML_REGISTERUNCREATABLE(N, T, MAJ, MIN) \
-    qmlRegisterUncreatableType<T>(N, MAJ, MIN, #T, "Don't try to add to a qml definition of " #T);
-
-#define QML_REGISTERUNCREATABLE_IN_NAMESPACE(T, NAMESPACE, MAJ, MIN) \
-    qmlRegisterUncreatableType<NAMESPACE::T>("net.jami.Models", \
-                                             MAJ, MIN, #T, \
+#define QML_REGISTERUNCREATABLE_IN_NAMESPACE(T, NAMESPACE) \
+    qmlRegisterUncreatableType<NAMESPACE::T>(NS_MODELS, \
+                                             VER_MAJ, VER_MIN, #T, \
                                              "Don't try to add to a qml definition of " #T);
 
+namespace Utils {
+
 /*!
  * This function will expose custom types to the QML engine.
  */
 void
-registerTypes(LRCInstance* instance)
+registerTypes()
 {
     // QAbstractListModels
-    QML_REGISTERTYPE("net.jami.Models", AccountListModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", DeviceItemListModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", BannedListModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", ModeratorListModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", MediaCodecListModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", AccountsToMigrateListModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", AudioDeviceModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", AudioManagerListModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", VideoInputDeviceModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", VideoFormatResolutionModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", VideoFormatFpsModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", PluginListPreferenceModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", SmartListModel, 1, 0);
+    QML_REGISTERTYPE(NS_MODELS, AccountListModel);
+    QML_REGISTERTYPE(NS_MODELS, DeviceItemListModel);
+    QML_REGISTERTYPE(NS_MODELS, BannedListModel);
+    QML_REGISTERTYPE(NS_MODELS, ModeratorListModel);
+    QML_REGISTERTYPE(NS_MODELS, MediaCodecListModel);
+    QML_REGISTERTYPE(NS_MODELS, AccountsToMigrateListModel);
+    QML_REGISTERTYPE(NS_MODELS, AudioDeviceModel);
+    QML_REGISTERTYPE(NS_MODELS, AudioManagerListModel);
+    QML_REGISTERTYPE(NS_MODELS, VideoInputDeviceModel);
+    QML_REGISTERTYPE(NS_MODELS, VideoFormatResolutionModel);
+    QML_REGISTERTYPE(NS_MODELS, VideoFormatFpsModel);
+    QML_REGISTERTYPE(NS_MODELS, PluginListPreferenceModel);
+    QML_REGISTERTYPE(NS_MODELS, SmartListModel);
 
     // QQuickItems
-    QML_REGISTERTYPE("net.jami.Models", PreviewRenderer, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", VideoCallPreviewRenderer, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", DistantRenderer, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", PhotoboothPreviewRender, 1, 0)
-
-    // Adaptors
-    QML_REGISTERSINGLETONTYPE_ADAPTER("net.jami.Adapters", CallAdapter, 1, 0);
-    QML_REGISTERSINGLETONTYPE_ADAPTER("net.jami.Adapters", MessagesAdapter, 1, 0);
-    QML_REGISTERSINGLETONTYPE_ADAPTER("net.jami.Adapters", ConversationsAdapter, 1, 0);
-    QML_REGISTERSINGLETONTYPE_ADAPTER("net.jami.Adapters", AvAdapter, 1, 0);
-    QML_REGISTERSINGLETONTYPE_ADAPTER("net.jami.Adapters", ContactAdapter, 1, 0);
-    QML_REGISTERSINGLETONTYPE_ADAPTER("net.jami.Adapters", PluginAdapter, 1, 0);
-    QML_REGISTERSINGLETONTYPE_ADAPTER("net.jami.Adapters", AccountAdapter, 1, 0);
-    QML_REGISTERSINGLETONTYPE_ADAPTER("net.jami.Adapters", UtilsAdapter, 1, 0);
-    QML_REGISTERSINGLETONTYPE_ADAPTER("net.jami.Adapters", SettingsAdapter, 1, 0);
-
-    QML_REGISTERSINGLETONTYPE_CUSTOM("net.jami.Models", AVModel, 1, 0, &instance->avModel())
-    QML_REGISTERSINGLETONTYPE_CUSTOM("net.jami.Models", PluginModel, 1, 0, &instance->pluginModel())
-
-    QML_REGISTERSINGLETONTYPE_CUSTOM("net.jami.Helpers", UpdateManager, 1, 0, instance->getUpdateManager())
+    QML_REGISTERTYPE(NS_MODELS, PreviewRenderer);
+    QML_REGISTERTYPE(NS_MODELS, VideoCallPreviewRenderer);
+    QML_REGISTERTYPE(NS_MODELS, DistantRenderer);
+    QML_REGISTERTYPE(NS_MODELS, PhotoboothPreviewRender)
 
     // Qml singleton components
-    QML_REGISTERSINGLETONTYPE_URL("net.jami.Constants", "qrc:/src/constant/JamiTheme.qml", JamiTheme, 1, 0);
-    QML_REGISTERSINGLETONTYPE_URL("net.jami.Models", "qrc:/src/constant/JamiQmlUtils.qml", JamiQmlUtils, 1, 0);
-    QML_REGISTERSINGLETONTYPE_URL("net.jami.Constants", "qrc:/src/constant/JamiStrings.qml", JamiStrings, 1, 0);
+    QML_REGISTERSINGLETONTYPE_URL(NS_CONSTANTS, "qrc:/src/constant/JamiTheme.qml", JamiTheme);
+    QML_REGISTERSINGLETONTYPE_URL(NS_MODELS, "qrc:/src/constant/JamiQmlUtils.qml", JamiQmlUtils);
+    QML_REGISTERSINGLETONTYPE_URL(NS_CONSTANTS, "qrc:/src/constant/JamiStrings.qml", JamiStrings);
 
     // C++ singletons
-    QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(NameDirectory, 1, 0);
+    // TODO: remove this
+    QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(NameDirectory);
 
     // Lrc namespaces, models, and singletons
-    QML_REGISTERNAMESPACE(lrc::api::staticMetaObject, "Lrc", 1, 0);
-    QML_REGISTERNAMESPACE(lrc::api::account::staticMetaObject, "Account", 1, 0);
-    QML_REGISTERNAMESPACE(lrc::api::call::staticMetaObject, "Call", 1, 0);
-    QML_REGISTERNAMESPACE(lrc::api::datatransfer::staticMetaObject, "Datatransfer", 1, 0);
-    QML_REGISTERNAMESPACE(lrc::api::interaction::staticMetaObject, "Interaction", 1, 0);
-    QML_REGISTERNAMESPACE(lrc::api::video::staticMetaObject, "Video", 1, 0);
-    QML_REGISTERNAMESPACE(lrc::api::profile::staticMetaObject, "Profile", 1, 0);
+    QML_REGISTERNAMESPACE(lrc::api::staticMetaObject, "Lrc");
+    QML_REGISTERNAMESPACE(lrc::api::account::staticMetaObject, "Account");
+    QML_REGISTERNAMESPACE(lrc::api::call::staticMetaObject, "Call");
+    QML_REGISTERNAMESPACE(lrc::api::datatransfer::staticMetaObject, "Datatransfer");
+    QML_REGISTERNAMESPACE(lrc::api::interaction::staticMetaObject, "Interaction");
+    QML_REGISTERNAMESPACE(lrc::api::video::staticMetaObject, "Video");
+    QML_REGISTERNAMESPACE(lrc::api::profile::staticMetaObject, "Profile");
 
     // Same as QML_REGISTERUNCREATABLE but omit the namespace in Qml
-    QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewAccountModel, lrc::api, 1, 0);
-    QML_REGISTERUNCREATABLE_IN_NAMESPACE(BehaviorController, lrc::api, 1, 0);
-    QML_REGISTERUNCREATABLE_IN_NAMESPACE(DataTransferModel, lrc::api, 1, 0);
-    QML_REGISTERUNCREATABLE_IN_NAMESPACE(ContactModel, lrc::api, 1, 0);
-    QML_REGISTERUNCREATABLE_IN_NAMESPACE(ConversationModel, lrc::api, 1, 0);
-    QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewCallModel, lrc::api, 1, 0);
-    QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewDeviceModel, lrc::api, 1, 0);
-    QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewCodecModel, lrc::api, 1, 0);
-    QML_REGISTERUNCREATABLE_IN_NAMESPACE(PeerDiscoveryModel, lrc::api, 1, 0);
+    QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewAccountModel, lrc::api);
+    QML_REGISTERUNCREATABLE_IN_NAMESPACE(BehaviorController, lrc::api);
+    QML_REGISTERUNCREATABLE_IN_NAMESPACE(DataTransferModel, lrc::api);
+    QML_REGISTERUNCREATABLE_IN_NAMESPACE(ContactModel, lrc::api);
+    QML_REGISTERUNCREATABLE_IN_NAMESPACE(ConversationModel, lrc::api);
+    QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewCallModel, lrc::api);
+    QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewDeviceModel, lrc::api);
+    QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewCodecModel, lrc::api);
+    QML_REGISTERUNCREATABLE_IN_NAMESPACE(PeerDiscoveryModel, lrc::api);
 
     // Enums
-    QML_REGISTERUNCREATABLE("net.jami.Enums", Settings, 1, 0);
-    QML_REGISTERUNCREATABLE("net.jami.Enums", NetWorkManager, 1, 0);
+    QML_REGISTERUNCREATABLE(NS_ENUMS, Settings);
+    QML_REGISTERUNCREATABLE(NS_ENUMS, NetWorkManager);
 }
 // clang-format on
+} // namespace Utils
diff --git a/src/qmlregister.h b/src/qmlregister.h
index 2b78ac055f165f15cd2027c98eaf217da99308ca..48591c30dd16192b613afb8697b5c47aebbf837e 100644
--- a/src/qmlregister.h
+++ b/src/qmlregister.h
@@ -18,6 +18,30 @@
 
 #pragma once
 
-class LRCInstance;
+#define NS_MODELS    "net.jami.Models"
+#define NS_ADAPTERS  "net.jami.Adapters"
+#define NS_CONSTANTS "net.jami.Constants"
+#define NS_HELPERS   "net.jami.Helpers"
+#define NS_ENUMS     "net.jami.Enums"
+#define VER_MAJ      1
+#define VER_MIN      0
 
-void registerTypes(LRCInstance* instance);
+// clang-format off
+#define QML_REGISTERSINGLETONTYPE_POBJECT(NS, I, N) \
+    QQmlEngine::setObjectOwnership(I, QQmlEngine::CppOwnership); \
+    { using T = std::remove_reference<decltype(*I)>::type; \
+    qmlRegisterSingletonType<T>(NS, VER_MAJ, VER_MIN, N, \
+                                [I](QQmlEngine*, QJSEngine*) -> QObject* { \
+                                    return I; }); }
+
+#define QML_REGISTERSINGLETONTYPE_CUSTOM(NS, T, P) \
+    qmlRegisterSingletonType<T>(NS, VER_MAJ, VER_MIN, #T, \
+                                [p=P](QQmlEngine* e, QJSEngine* se) -> QObject* { \
+                                    Q_UNUSED(e); Q_UNUSED(se); \
+                                    return p; \
+                                });
+// clang-format on
+
+namespace Utils {
+void registerTypes();
+}
diff --git a/src/settingsadapter.cpp b/src/settingsadapter.cpp
index 34551a1c9fc501185591943c32a1f99a47ecd5fd..a2ea168b68431f5fca3fc86e22d982e4a35b58d4 100644
--- a/src/settingsadapter.cpp
+++ b/src/settingsadapter.cpp
@@ -20,8 +20,11 @@
 
 #include "api/newdevicemodel.h"
 
-SettingsAdapter::SettingsAdapter(LRCInstance* instance, QObject* parent)
+SettingsAdapter::SettingsAdapter(AppSettingsManager* settingsManager,
+                                 LRCInstance* instance,
+                                 QObject* parent)
     : QmlAdapterBase(instance, parent)
+    , settingsManager_(settingsManager)
 {}
 
 QString
@@ -54,13 +57,13 @@ SettingsAdapter::getDir_Download()
 QVariant
 SettingsAdapter::getAppValue(const Settings::Key key)
 {
-    return AppSettingsManager::getValue(key);
+    return settingsManager_->getValue(key);
 }
 
 void
 SettingsAdapter::setAppValue(const Settings::Key key, const QVariant& value)
 {
-    AppSettingsManager::setValue(key, value);
+    settingsManager_->setValue(key, value);
 }
 
 void
diff --git a/src/settingsadapter.h b/src/settingsadapter.h
index 0e164f2cae1722ce7b1c3c1943f3b60520d4b873..01815c06db23e31e004907281dfdb3f530aae79f 100644
--- a/src/settingsadapter.h
+++ b/src/settingsadapter.h
@@ -19,7 +19,6 @@
 #pragma once
 
 #include <QObject>
-#include <QSettings>
 
 #include "api/account.h"
 #include "api/datatransfermodel.h"
@@ -27,12 +26,15 @@
 #include "typedefs.h"
 #include "utils.h"
 #include "qmladapterbase.h"
+#include "appsettingsmanager.h"
 
 class SettingsAdapter : public QmlAdapterBase
 {
     Q_OBJECT
 public:
-    explicit SettingsAdapter(LRCInstance* instance, QObject* parent = nullptr);
+    explicit SettingsAdapter(AppSettingsManager* settingsManager,
+                             LRCInstance* instance,
+                             QObject* parent = nullptr);
 
     void safeInit() override {}
 
@@ -225,5 +227,8 @@ public:
     Q_INVOKABLE void enableLocalModerators(const QString& accountID, const bool& isModEnabled);
     Q_INVOKABLE bool isLocalModeratorsEnabled(const QString& accountId);
     Q_INVOKABLE bool isAllModeratorsEnabled(const QString& accountId);
+
+private:
+    AppSettingsManager* settingsManager_;
 };
 Q_DECLARE_METATYPE(SettingsAdapter*)
diff --git a/src/systemtray.cpp b/src/systemtray.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c64c3c88ca0a3bcad7582252d6a0d7a4509b685a
--- /dev/null
+++ b/src/systemtray.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "systemtray.h"
+
+#include "appsettingsmanager.h"
+
+SystemTray::SystemTray(AppSettingsManager* settingsManager, QObject* parent)
+    : QSystemTrayIcon(parent)
+    , settingsManager_(settingsManager)
+{}
+
+SystemTray::~SystemTray()
+{
+    hide();
+}
+
+void
+SystemTray::showNotification(const QString& message,
+                             const QString& from,
+                             std::function<void()> const& onClickedCb)
+{
+    if (!settingsManager_->getValue(Settings::Key::EnableNotifications).toBool()) {
+        qWarning() << "Notifications are disabled";
+        return;
+    }
+
+    setOnClickedCallback(std::move(onClickedCb));
+
+    if (from.isEmpty())
+        showMessage(message, "", QIcon(":images/jami.png"));
+    else
+        showMessage(from, message, QIcon(":images/jami.png"));
+}
+
+template<typename Func>
+void
+SystemTray::setOnClickedCallback(Func&& onClicked)
+{
+    disconnect(messageClicked_);
+    messageClicked_ = connect(this, &QSystemTrayIcon::messageClicked, onClicked);
+}
diff --git a/src/globalsystemtray.h b/src/systemtray.h
similarity index 54%
rename from src/globalsystemtray.h
rename to src/systemtray.h
index 2979a6577dc37452d8303d77d8b6f7fb663a9938..558715b8c3874441a67e40e4b6868f68816ce629 100644
--- a/src/globalsystemtray.h
+++ b/src/systemtray.h
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2015-2020 by Savoir-faire Linux
- * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,33 +18,26 @@
 
 #pragma once
 
-#include "lrcinstance.h"
-
 #include <QSystemTrayIcon>
 
-class GlobalSystemTray final : public QSystemTrayIcon
+class AppSettingsManager;
+
+class SystemTray final : public QSystemTrayIcon
 {
     Q_OBJECT
 
 public:
-    ~GlobalSystemTray() = default;
-    static GlobalSystemTray& instance()
-    {
-        static GlobalSystemTray* instance_ = new GlobalSystemTray();
-        return *instance_;
-    }
+    explicit SystemTray(AppSettingsManager* settingsManager, QObject* parent = nullptr);
+    ~SystemTray();
+
+    void showNotification(const QString& message,
+                          const QString& from,
+                          std::function<void()> const& onClickedCb);
 
     template<typename Func>
-    static void connectClicked(Func&& onClicked)
-    {
-        auto& instance_ = instance();
-        instance_.disconnect(instance_.messageClicked_);
-        instance_.connect(&instance_, &QSystemTrayIcon::messageClicked, onClicked);
-    }
+    void setOnClickedCallback(Func&& onClickedCb);
 
 private:
-    explicit GlobalSystemTray()
-        : QSystemTrayIcon() {};
-
     QMetaObject::Connection messageClicked_;
+    AppSettingsManager* settingsManager_;
 };
diff --git a/src/utils.cpp b/src/utils.cpp
index bac7ab799294aba883952552bc0fcf2a6fc72018..46081a1c7375f16b4368ac734340b42225e52cbe 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -22,7 +22,6 @@
 
 #include "utils.h"
 
-#include "globalsystemtray.h"
 #include "jamiavatartheme.h"
 #include "lrcinstance.h"
 
@@ -395,31 +394,6 @@ Utils::getCirclePhoto(const QImage original, int sizePhoto)
     return target;
 }
 
-void
-Utils::showNotification(const QString& message,
-                        const QString& from,
-                        const QString& accountId,
-                        const QString& convUid,
-                        std::function<void()> const& onClicked)
-{
-    if (accountId.isEmpty() || convUid.isEmpty()) {
-        // This should never happen.
-        qFatal("Invalid account or conversation.");
-    }
-
-    if (!AppSettingsManager::getValue(Settings::Key::EnableNotifications).toBool()) {
-        qWarning() << "Notifications are disabled";
-        return;
-    }
-
-    GlobalSystemTray::connectClicked(std::move(onClicked));
-
-    if (from.isEmpty())
-        GlobalSystemTray::instance().showMessage(message, "", QIcon(":images/jami.png"));
-    else
-        GlobalSystemTray::instance().showMessage(from, message, QIcon(":images/jami.png"));
-}
-
 QSize
 Utils::getRealSize(QScreen* screen)
 {
diff --git a/src/utils.h b/src/utils.h
index e39439d9d3efff56e91f778896f4611f5952c582..b56b888baa45775219e95b3b2c061313dfe87620 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -66,11 +66,6 @@ const char* WinGetEnv(const char* name);
 QString GetRingtonePath();
 QString GenGUID();
 QString GetISODate();
-void showNotification(const QString& message,
-                      const QString& from,
-                      const QString& accountId,
-                      const QString& convUid,
-                      std::function<void()> const& onClicked);
 QSize getRealSize(QScreen* screen);
 void forceDeleteAsync(const QString& path);
 QString getProjectCredits();
diff --git a/src/utilsadapter.cpp b/src/utilsadapter.cpp
index a1545cb3bb142fbbec3aca8e416bf9c0f3bdd8a9..e09c3be282202278702dcb773c6ff94575ad4aa7 100644
--- a/src/utilsadapter.cpp
+++ b/src/utilsadapter.cpp
@@ -22,8 +22,8 @@
 
 #include "utilsadapter.h"
 
-#include "globalsystemtray.h"
 #include "lrcinstance.h"
+#include "systemtray.h"
 #include "utils.h"
 #include "version.h"
 
@@ -31,9 +31,10 @@
 #include <QClipboard>
 #include <QFileInfo>
 
-UtilsAdapter::UtilsAdapter(LRCInstance* instance, QObject* parent)
+UtilsAdapter::UtilsAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent)
     : QmlAdapterBase(instance, parent)
     , clipboard_(QApplication::clipboard())
+    , systemTray_(systemTray)
 {}
 
 const QString
@@ -360,5 +361,5 @@ UtilsAdapter::humanFileSize(qint64 fileSize)
 void
 UtilsAdapter::setSystemTrayIconVisible(bool visible)
 {
-    GlobalSystemTray::instance().setVisible(visible);
+    systemTray_->setVisible(visible);
 }
diff --git a/src/utilsadapter.h b/src/utilsadapter.h
index 4eb730159a97e6a51d91db83adaf0a9c63582c0c..82daf6292f177de26d417d3097e942499e603449 100644
--- a/src/utilsadapter.h
+++ b/src/utilsadapter.h
@@ -28,12 +28,13 @@
 #include "qmladapterbase.h"
 
 class QClipboard;
+class SystemTray;
 
 class UtilsAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
 public:
-    explicit UtilsAdapter(LRCInstance* instance, QObject* parent = nullptr);
+    explicit UtilsAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent = nullptr);
     ~UtilsAdapter() = default;
 
     void safeInit() override {}
@@ -81,5 +82,6 @@ public:
 
 private:
     QClipboard* clipboard_;
+    SystemTray* systemTray_;
 };
 Q_DECLARE_METATYPE(UtilsAdapter*)