diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3e5b4002f78d4b83320b998b5e84bc1d49f55c12..1bb35818d3d11bb9a0aa5b9d0f3605acd8eb014f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -74,7 +74,8 @@ set(COMMON_SOURCES
     ${SRC_DIR}/moderatorlistmodel.cpp
     ${SRC_DIR}/screensaver.cpp
     ${SRC_DIR}/systemtray.cpp
-    ${SRC_DIR}/appsettingsmanager.cpp)
+    ${SRC_DIR}/appsettingsmanager.cpp
+    ${SRC_DIR}/lrcinstance.cpp)
 
 set(COMMON_HEADERS
     ${SRC_DIR}/avatarimageprovider.h
@@ -86,7 +87,6 @@ set(COMMON_HEADERS
     ${SRC_DIR}/version.h
     ${SRC_DIR}/accountlistmodel.h
     ${SRC_DIR}/runguard.h
-    ${SRC_DIR}/lrcinstance.h
     ${SRC_DIR}/webchathelpers.h
     ${SRC_DIR}/rendermanager.h
     ${SRC_DIR}/connectivitymonitor.h
@@ -127,7 +127,8 @@ set(COMMON_HEADERS
     ${SRC_DIR}/moderatorlistmodel.h
     ${SRC_DIR}/screensaver.h
     ${SRC_DIR}/systemtray.h
-    ${SRC_DIR}/appsettingsmanager.h)
+    ${SRC_DIR}/appsettingsmanager.h
+    ${SRC_DIR}/lrcinstance.h)
 
 find_package(PkgConfig REQUIRED)
 
diff --git a/jami-qt.pro b/jami-qt.pro
index 5ad3e8ff0eb26713d6fa2930667a1c1d00bca59b..9f0417c63f01568b0f9b65c6fe931117fe9c39a2 100644
--- a/jami-qt.pro
+++ b/jami-qt.pro
@@ -104,7 +104,6 @@ HEADERS += \
         src/version.h \
         src/accountlistmodel.h \
         src/runguard.h \
-        src/lrcinstance.h \
         src/webchathelpers.h \
         src/rendermanager.h \
         src/connectivitymonitor.h \
@@ -139,7 +138,8 @@ HEADERS += \
         src/qtutils.h \
         src/utilsadapter.h \
         src/systemtray.h \
-        src/appsettingsmanager.h
+        src/appsettingsmanager.h \
+        src/lrcinstance.h
 
 SOURCES += \
         src/bannedlistmodel.cpp \
@@ -181,7 +181,8 @@ SOURCES += \
         src/qmlregister.cpp \
         src/utilsadapter.cpp \
         src/systemtray.cpp \
-        src/appsettingsmanager.cpp
+        src/appsettingsmanager.cpp \
+        src/lrcinstance.cpp
 
 RESOURCES += \
         resources.qrc \
diff --git a/qml.qrc b/qml.qrc
index dc35418b24c6a6fa8255d5f05bde13863c5bc37b..1f5a159bad79061cfd80dbe317ba23d0b4da82a2 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -1,6 +1,40 @@
 <RCC>
     <qresource prefix="/">
+        <file>src/MainApplicationWindow.qml</file>
+        <file>src/DaemonReconnectWindow.qml</file>
+        <file>src/constant/JamiQmlUtils.qml</file>
         <file>src/constant/JamiStrings.qml</file>
+        <file>src/constant/JamiTheme.qml</file>
+        <file>src/commoncomponents/SettingParaCombobox.qml</file>
+        <file>src/commoncomponents/PreferenceItemDelegate.qml</file>
+        <file>src/commoncomponents/PasswordDialog.qml</file>
+        <file>src/commoncomponents/MaterialLineEdit.qml</file>
+        <file>src/commoncomponents/PhotoboothView.qml</file>
+        <file>src/commoncomponents/LookupStatusLabel.qml</file>
+        <file>src/commoncomponents/ListViewJami.qml</file>
+        <file>src/commoncomponents/DeleteAccountDialog.qml</file>
+        <file>src/commoncomponents/CustomBorder.qml</file>
+        <file>src/commoncomponents/PushButton.qml</file>
+        <file>src/commoncomponents/JamiFileDialog.qml</file>
+        <file>src/commoncomponents/TintedButton.qml</file>
+        <file>src/commoncomponents/GeneralMenuItem.qml</file>
+        <file>src/commoncomponents/GeneralMenuSeparator.qml</file>
+        <file>src/commoncomponents/AccountMigrationDialog.qml</file>
+        <file>src/commoncomponents/MaterialButton.qml</file>
+        <file>src/commoncomponents/ElidedTextLabel.qml</file>
+        <file>src/commoncomponents/js/contextmenugenerator.js</file>
+        <file>src/commoncomponents/BaseContextMenu.qml</file>
+        <file>src/commoncomponents/SpinnerButton.qml</file>
+        <file>src/commoncomponents/UsernameLineEdit.qml</file>
+        <file>src/commoncomponents/Scaffold.qml</file>
+        <file>src/commoncomponents/LineEditContextMenu.qml</file>
+        <file>src/commoncomponents/BaseDialog.qml</file>
+        <file>src/commoncomponents/ModalPopup.qml</file>
+        <file>src/commoncomponents/SimpleMessageDialog.qml</file>
+        <file>src/commoncomponents/ResponsiveImage.qml</file>
+        <file>src/commoncomponents/PresenceIndicator.qml</file>
+        <file>src/commoncomponents/AvatarImage.qml</file>
+        <file>src/commoncomponents/DaemonReconnectPopup.qml</file>
         <file>src/settingsview/SettingsView.qml</file>
         <file>src/settingsview/components/ChatviewSettings.qml</file>
         <file>src/settingsview/components/SettingsMenu.qml</file>
@@ -42,22 +76,13 @@
         <file>src/settingsview/components/SettingsComboBox.qml</file>
         <file>src/settingsview/components/SettingsMaterialLineEdit.qml</file>
         <file>src/settingsview/components/LevelMeter.qml</file>
-        <file>src/commoncomponents/SettingParaCombobox.qml</file>
         <file>src/settingsview/components/DeviceItemDelegate.qml</file>
         <file>src/settingsview/components/PluginItemDelegate.qml</file>
-        <file>src/mainview/components/PluginHandlerItemDelegate.qml</file>
-        <file>src/commoncomponents/PreferenceItemDelegate.qml</file>
         <file>src/settingsview/components/ContactItemDelegate.qml</file>
         <file>src/settingsview/components/MediaCodecDelegate.qml</file>
         <file>src/settingsview/components/NameRegistrationDialog.qml</file>
         <file>src/settingsview/components/LinkDeviceDialog.qml</file>
         <file>src/settingsview/components/RevokeDevicePasswordDialog.qml</file>
-        <file>src/commoncomponents/PasswordDialog.qml</file>
-        <file>src/commoncomponents/MaterialLineEdit.qml</file>
-        <file>src/commoncomponents/PhotoboothView.qml</file>
-        <file>src/commoncomponents/LookupStatusLabel.qml</file>
-        <file>src/commoncomponents/ListViewJami.qml</file>
-        <file>src/commoncomponents/DeleteAccountDialog.qml</file>
         <file>src/wizardview/WizardView.qml</file>
         <file>src/wizardview/components/WelcomePage.qml</file>
         <file>src/wizardview/components/CreateAccountPage.qml</file>
@@ -68,27 +93,22 @@
         <file>src/wizardview/components/ConnectToAccountManagerPage.qml</file>
         <file>src/wizardview/components/ProfilePage.qml</file>
         <file>src/wizardview/components/CollapsiblePasswordWidget.qml</file>
-        <file>src/MainApplicationWindow.qml</file>
+        <file>src/wizardview/components/AccountCreationStepIndicator.qml</file>
         <file>src/mainview/MainView.qml</file>
-        <file>src/commoncomponents/CustomBorder.qml</file>
-        <file>src/constant/JamiTheme.qml</file>
+        <file>src/mainview/components/PluginHandlerItemDelegate.qml</file>
         <file>src/mainview/components/AboutPopUp.qml</file>
         <file>src/mainview/components/SidePanel.qml</file>
         <file>src/mainview/components/WelcomePage.qml</file>
         <file>src/mainview/components/MessageWebView.qml</file>
         <file>src/mainview/components/MessageWebViewHeader.qml</file>
-        <file>src/commoncomponents/PushButton.qml</file>
         <file>src/mainview/components/AccountComboBox.qml</file>
         <file>src/mainview/components/ConversationSmartListView.qml</file>
-        <file>src/commoncomponents/JamiFileDialog.qml</file>
         <file>src/mainview/components/CallStackView.qml</file>
         <file>src/mainview/components/IncomingCallPage.qml</file>
         <file>src/mainview/components/OutgoingCallPage.qml</file>
         <file>src/mainview/components/AudioCallPage.qml</file>
         <file>src/mainview/components/CallOverlay.qml</file>
-        <file>src/commoncomponents/TintedButton.qml</file>
         <file>src/mainview/components/CallOverlayButtonGroup.qml</file>
-        <file>src/mainview/js/incomingcallpagecreation.js</file>
         <file>src/mainview/components/ContactSearchBar.qml</file>
         <file>src/mainview/components/VideoCallPage.qml</file>
         <file>src/mainview/components/ParticipantOverlay.qml</file>
@@ -97,46 +117,26 @@
         <file>src/mainview/components/ConversationSmartListViewItemDelegate.qml</file>
         <file>src/mainview/components/SidePanelTabBar.qml</file>
         <file>src/mainview/components/WelcomePageQrDialog.qml</file>
-        <file>src/commoncomponents/GeneralMenuItem.qml</file>
         <file>src/mainview/components/ConversationSmartListContextMenu.qml</file>
         <file>src/mainview/components/CallViewContextMenu.qml</file>
-        <file>src/commoncomponents/GeneralMenuSeparator.qml</file>
         <file>src/mainview/components/UserProfile.qml</file>
-        <file>src/mainview/js/videodevicecontextmenuitemcreation.js</file>
         <file>src/mainview/components/VideoCallPageContextMenuDeviceItem.qml</file>
         <file>src/mainview/components/SelectScreen.qml</file>
-        <file>src/mainview/js/selectscreenwindowcreation.js</file>
         <file>src/mainview/components/ScreenRubberBand.qml</file>
-        <file>src/mainview/js/screenrubberbandcreation.js</file>
-        <file>src/mainview/js/callfullscreenwindowcontainercreation.js</file>
         <file>src/mainview/components/VideoCallFullScreenWindowContainer.qml</file>
         <file>src/mainview/components/ContactPicker.qml</file>
         <file>src/mainview/components/PluginHandlerPicker.qml</file>
-        <file>src/mainview/js/contactpickercreation.js</file>
-        <file>src/mainview/js/pluginhandlerpickercreation.js</file>
         <file>src/mainview/components/ContactPickerItemDelegate.qml</file>
-        <file>src/commoncomponents/AccountMigrationDialog.qml</file>
-        <file>src/commoncomponents/MaterialButton.qml</file>
         <file>src/mainview/components/RecordBox.qml</file>
-        <file>src/commoncomponents/ElidedTextLabel.qml</file>
         <file>src/mainview/components/SipInputPanel.qml</file>
-        <file>src/commoncomponents/js/contextmenugenerator.js</file>
-        <file>src/commoncomponents/BaseContextMenu.qml</file>
-        <file>src/commoncomponents/Scaffold.qml</file>
-        <file>src/constant/JamiQmlUtils.qml</file>
-        <file>src/wizardview/components/AccountCreationStepIndicator.qml</file>
-        <file>src/commoncomponents/SpinnerButton.qml</file>
-        <file>src/commoncomponents/UsernameLineEdit.qml</file>
-        <file>src/mainview/components/UserInfoCallPage.qml</file>
-        <file>src/commoncomponents/BaseDialog.qml</file>
-        <file>src/commoncomponents/ModalPopup.qml</file>
-        <file>src/commoncomponents/SimpleMessageDialog.qml</file>
-        <file>src/commoncomponents/ResponsiveImage.qml</file>
-        <file>src/commoncomponents/PresenceIndicator.qml</file>
-        <file>src/commoncomponents/AvatarImage.qml</file>
         <file>src/mainview/components/ParticipantOverlayMenu.qml</file>
-        <file>src/commoncomponents/DaemonReconnectPopup.qml</file>
-        <file>src/DaemonReconnectWindow.qml</file>
-        <file>src/commoncomponents/LineEditContextMenu.qml</file>
+        <file>src/mainview/components/UserInfoCallPage.qml</file>
+        <file>src/mainview/js/videodevicecontextmenuitemcreation.js</file>
+        <file>src/mainview/js/incomingcallpagecreation.js</file>
+        <file>src/mainview/js/selectscreenwindowcreation.js</file>
+        <file>src/mainview/js/screenrubberbandcreation.js</file>
+        <file>src/mainview/js/callfullscreenwindowcontainercreation.js</file>
+        <file>src/mainview/js/contactpickercreation.js</file>
+        <file>src/mainview/js/pluginhandlerpickercreation.js</file>
     </qresource>
 </RCC>
diff --git a/src/calladapter.cpp b/src/calladapter.cpp
index 9a3b4495c5edfd3b3d125114c8ac42e8cb819c67..6f5cba4fa47ea2239196e068c2cde52d0bb041ad 100644
--- a/src/calladapter.cpp
+++ b/src/calladapter.cpp
@@ -28,6 +28,8 @@
 #include "utils.h"
 
 #include <QApplication>
+#include <QTimer>
+#include <QJsonObject>
 
 CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent)
     : QmlAdapterBase(instance, parent)
@@ -475,7 +477,7 @@ CallAdapter::connectCallModel(const QString& accountId)
                  */
                 bool forceCallOnly {false};
                 if (!convInfo.confId.isEmpty()) {
-                    auto callList = lrcInstance_->getAPI().getConferenceSubcalls(convInfo.confId);
+                    auto callList = lrcInstance_->getConferenceSubcalls(convInfo.confId);
                     if (callList.empty()) {
                         auto lastConference = lrcInstance_->poplastConference(convInfo.confId);
                         if (!lastConference.isEmpty()) {
@@ -612,7 +614,7 @@ CallAdapter::hangupCall(const QString& uri)
              * so we can switch the smartlist index after termination.
              */
             if (!convInfo.confId.isEmpty()) {
-                auto callList = lrcInstance_->getAPI().getConferenceSubcalls(convInfo.confId);
+                auto callList = lrcInstance_->getConferenceSubcalls(convInfo.confId);
                 if (callList.size() == 2) {
                     for (const auto& cId : callList) {
                         if (cId != convInfo.callId) {
diff --git a/src/lrcinstance.cpp b/src/lrcinstance.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..deed90833b61838dbfd5d3ea733f88f87ee67ecd
--- /dev/null
+++ b/src/lrcinstance.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2019-2021 by Savoir-faire Linux
+ * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
+ * Author: Isa Nanic <isa.nanic@savoirfairelinux.com>
+ * Author: Mingrui Zhang <mingrui.zhang@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 "lrcinstance.h"
+
+#include <QBuffer>
+#include <QMutex>
+#include <QObject>
+#include <QPixmap>
+#include <QRegularExpression>
+#include <QSettings>
+#include <QtConcurrent/QtConcurrent>
+
+LRCInstance::LRCInstance(migrateCallback willMigrateCb,
+                         migrateCallback didMigrateCb,
+                         const QString& updateUrl,
+                         ConnectivityMonitor* connectivityMonitor)
+    : lrc_(std::make_unique<Lrc>(willMigrateCb, didMigrateCb))
+    , renderer_(std::make_unique<RenderManager>(lrc_->getAVModel()))
+    , updateManager_(std::make_unique<UpdateManager>(updateUrl, connectivityMonitor, this))
+{
+    lrc_->holdConferences = false;
+};
+
+VectorString
+LRCInstance::getConferenceSubcalls(const QString& callId)
+{
+    return lrc_->getConferenceSubcalls(callId);
+}
+
+RenderManager*
+LRCInstance::renderer()
+{
+    return renderer_.get();
+}
+
+UpdateManager*
+LRCInstance::getUpdateManager()
+{
+    return updateManager_.get();
+}
+
+void
+LRCInstance::connectivityChanged()
+{
+    lrc_->connectivityChanged();
+}
+
+NewAccountModel&
+LRCInstance::accountModel()
+{
+    return lrc_->getAccountModel();
+}
+
+BehaviorController&
+LRCInstance::behaviorController()
+{
+    return lrc_->getBehaviorController();
+}
+
+DataTransferModel&
+LRCInstance::dataTransferModel()
+{
+    return lrc_->getDataTransferModel();
+}
+
+AVModel&
+LRCInstance::avModel()
+{
+    return lrc_->getAVModel();
+}
+
+PluginModel&
+LRCInstance::pluginModel()
+{
+    return lrc_->getPluginModel();
+}
+
+bool
+LRCInstance::isConnected()
+{
+    return lrc_->isConnected();
+}
+
+VectorString
+LRCInstance::getActiveCalls()
+{
+    return lrc_->activeCalls();
+}
+
+const account::Info&
+LRCInstance::getAccountInfo(const QString& accountId)
+{
+    return accountModel().getAccountInfo(accountId);
+}
+
+const account::Info&
+LRCInstance::getCurrentAccountInfo()
+{
+    return getAccountInfo(getCurrAccId());
+}
+
+bool
+LRCInstance::hasVideoCall()
+{
+    auto activeCalls = lrc_->activeCalls();
+    auto accountList = accountModel().getAccountList();
+    bool result = false;
+    for (const auto& callId : activeCalls) {
+        for (const auto& accountId : accountList) {
+            auto& accountInfo = accountModel().getAccountInfo(accountId);
+            if (accountInfo.callModel->hasCall(callId)) {
+                auto call = accountInfo.callModel->getCall(callId);
+                result |= !(call.isAudioOnly || call.videoMuted);
+            }
+        }
+    }
+    return result;
+}
+
+QString
+LRCInstance::getCallIdForConversationUid(const QString& convUid, const QString& accountId)
+{
+    const auto& convInfo = getConversationFromConvUid(convUid, accountId);
+    if (convInfo.uid.isEmpty()) {
+        return {};
+    }
+    return convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId;
+}
+
+const call::Info*
+LRCInstance::getCallInfo(const QString& callId, const QString& accountId)
+{
+    try {
+        auto& accInfo = accountModel().getAccountInfo(accountId);
+        if (!accInfo.callModel->hasCall(callId)) {
+            return nullptr;
+        }
+        return &accInfo.callModel->getCall(callId);
+    } catch (...) {
+        return nullptr;
+    }
+}
+
+const call::Info*
+LRCInstance::getCallInfoForConversation(const conversation::Info& convInfo, bool forceCallOnly)
+{
+    try {
+        auto accountId = convInfo.accountId;
+        auto& accInfo = accountModel().getAccountInfo(accountId);
+        auto callId = forceCallOnly
+                          ? convInfo.callId
+                          : (convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId);
+        if (!accInfo.callModel->hasCall(callId)) {
+            return nullptr;
+        }
+        return &accInfo.callModel->getCall(callId);
+    } catch (...) {
+        return nullptr;
+    }
+}
+
+const conversation::Info&
+LRCInstance::getConversationFromConvUid(const QString& convUid, const QString& accountId)
+{
+    auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId : getCurrAccId());
+    auto& convModel = accInfo.conversationModel;
+    return convModel->getConversationForUid(convUid).value_or(invalid);
+}
+
+const conversation::Info&
+LRCInstance::getConversationFromPeerUri(const QString& peerUri, const QString& accountId)
+{
+    auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId : getCurrAccId());
+    auto& convModel = accInfo.conversationModel;
+    return convModel->getConversationForPeerUri(peerUri).value_or(invalid);
+}
+
+const conversation::Info&
+LRCInstance::getConversationFromCallId(const QString& callId, const QString& accountId)
+{
+    auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId : getCurrAccId());
+    auto& convModel = accInfo.conversationModel;
+    return convModel->getConversationForCallId(callId).value_or(invalid);
+}
+
+ConversationModel*
+LRCInstance::getCurrentConversationModel()
+{
+    return getCurrentAccountInfo().conversationModel.get();
+}
+
+NewCallModel*
+LRCInstance::getCurrentCallModel()
+{
+    return getCurrentAccountInfo().callModel.get();
+}
+
+const QString&
+LRCInstance::getCurrAccId()
+{
+    if (selectedAccountId_.isEmpty()) {
+        auto accountList = accountModel().getAccountList();
+        if (accountList.size())
+            selectedAccountId_ = accountList.at(0);
+    }
+    return selectedAccountId_;
+}
+
+void
+LRCInstance::setSelectedAccountId(const QString& accountId)
+{
+    if (accountId == selectedAccountId_)
+        return; // No need to select current selected account
+
+    selectedAccountId_ = accountId;
+
+    // Last selected account should be set as preferred.
+    accountModel().setTopAccount(accountId);
+
+    Q_EMIT currentAccountChanged();
+}
+
+const QString&
+LRCInstance::getCurrentConvUid()
+{
+    return selectedConvUid_;
+}
+
+void
+LRCInstance::setSelectedConvId(const QString& convUid)
+{
+    selectedConvUid_ = convUid;
+}
+
+int
+LRCInstance::getCurrentAccountIndex()
+{
+    for (int i = 0; i < accountModel().getAccountList().size(); i++) {
+        if (accountModel().getAccountList()[i] == getCurrAccId()) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+void
+LRCInstance::setAvatarForAccount(const QPixmap& avatarPixmap, const QString& accountID)
+{
+    QByteArray ba;
+    QBuffer bu(&ba);
+    bu.open(QIODevice::WriteOnly);
+    avatarPixmap.save(&bu, "PNG");
+    auto str = QString::fromLocal8Bit(ba.toBase64());
+    accountModel().setAvatar(accountID, str);
+}
+
+void
+LRCInstance::setCurrAccAvatar(const QPixmap& avatarPixmap)
+{
+    QByteArray ba;
+    QBuffer bu(&ba);
+    bu.open(QIODevice::WriteOnly);
+    avatarPixmap.save(&bu, "PNG");
+    auto str = QString::fromLocal8Bit(ba.toBase64());
+    accountModel().setAvatar(getCurrAccId(), str);
+}
+
+void
+LRCInstance::setCurrAccAvatar(const QString& avatar)
+{
+    accountModel().setAvatar(getCurrAccId(), avatar);
+}
+
+void
+LRCInstance::setCurrAccDisplayName(const QString& displayName)
+{
+    auto accountId = getCurrAccId();
+    accountModel().setAlias(accountId, displayName);
+    /*
+     * Force save to .yml.
+     */
+    auto confProps = accountModel().getAccountConfig(accountId);
+    accountModel().setAccountConfig(accountId, confProps);
+}
+
+const account::ConfProperties_t&
+LRCInstance::getCurrAccConfig()
+{
+    return getCurrentAccountInfo().confProperties;
+}
+
+void
+LRCInstance::subscribeToDebugReceived()
+{
+    lrc_->subscribeToDebugReceived();
+}
+
+void
+LRCInstance::startAudioMeter(bool async)
+{
+    auto f = [this] {
+        if (!getActiveCalls().size()) {
+            avModel().startAudioDevice();
+        }
+        avModel().setAudioMeterState(true);
+    };
+    if (async) {
+        QtConcurrent::run(f);
+    } else {
+        f();
+    }
+}
+
+void
+LRCInstance::stopAudioMeter(bool async)
+{
+    auto f = [this] {
+        if (!getActiveCalls().size()) {
+            avModel().stopAudioDevice();
+        }
+        avModel().setAudioMeterState(false);
+    };
+    if (async) {
+        QtConcurrent::run(f);
+    } else {
+        f();
+    }
+}
+
+QString
+LRCInstance::getContentDraft(const QString& convUid, const QString& accountId)
+{
+    auto draftKey = accountId + "_" + convUid;
+    return contentDrafts_[draftKey];
+}
+
+void
+LRCInstance::setContentDraft(const QString& convUid,
+                             const QString& accountId,
+                             const QString& content)
+{
+    auto draftKey = accountId + "_" + convUid;
+    contentDrafts_[draftKey] = content;
+}
+
+void
+LRCInstance::pushlastConference(const QString& confId, const QString& callId)
+{
+    lastConferences_[confId] = callId;
+}
+
+QString
+LRCInstance::poplastConference(const QString& confId)
+{
+    QString callId = {};
+    auto iter = lastConferences_.find(confId);
+    if (iter != lastConferences_.end()) {
+        callId = iter.value();
+        lastConferences_.erase(iter);
+    }
+    return callId;
+}
+
+void
+LRCInstance::selectConversation(const QString& accountId, const QString& convUid)
+{
+    const auto& convInfo = getConversationFromConvUid(convUid, accountId);
+
+    if (getCurrentConvUid() != convInfo.uid || convInfo.participants.size() > 0) {
+        // If the account is not currently selected, do that first, then
+        // proceed to select the conversation.
+        auto selectConversation = [this, accountId, convUid = convInfo.uid] {
+            const auto& convInfo = getConversationFromConvUid(convUid, accountId);
+            if (convInfo.uid.isEmpty()) {
+                return;
+            }
+            auto& accInfo = getAccountInfo(convInfo.accountId);
+            setSelectedConvId(convInfo.uid);
+            accInfo.conversationModel->clearUnreadInteractions(convInfo.uid);
+
+            try {
+                // Set contact filter (for conversation tab selection)
+                auto& contact = accInfo.contactModel->getContact(convInfo.participants.front());
+                setProperty("currentTypeFilter", QVariant::fromValue(contact.profileInfo.type));
+            } catch (const std::out_of_range& e) {
+                qDebug() << e.what();
+            }
+        };
+        if (convInfo.accountId != getCurrAccId()) {
+            Utils::oneShotConnect(this, &LRCInstance::currentAccountChanged, [selectConversation] {
+                selectConversation();
+            });
+            setSelectedConvId();
+            setSelectedAccountId(convInfo.accountId);
+        } else {
+            selectConversation();
+        }
+    }
+    Q_EMIT conversationSelected();
+}
+
+void
+LRCInstance::finish()
+{
+    renderer_.reset();
+    lrc_.reset();
+}
diff --git a/src/lrcinstance.h b/src/lrcinstance.h
index e5d8ccdc36068a88c7cb94e6774e7f9310b8f119..f50ba8688d87ad0d56f4b5be09aed35cb582f5ec 100644
--- a/src/lrcinstance.h
+++ b/src/lrcinstance.h
@@ -1,5 +1,5 @@
-/*!
- * Copyright (C) 2019-2020 by Savoir-faire Linux
+/*
+ * Copyright (C) 2019-2021 by Savoir-faire Linux
  * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
  * Author: Isa Nanic <isa.nanic@savoirfairelinux.com>
  * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
@@ -29,29 +29,18 @@
 #include "qtutils.h"
 #include "utils.h"
 
+#include "api/lrc.h"
 #include "api/account.h"
 #include "api/avmodel.h"
-#include "api/pluginmodel.h"
 #include "api/behaviorcontroller.h"
 #include "api/contact.h"
 #include "api/contactmodel.h"
 #include "api/conversation.h"
 #include "api/conversationmodel.h"
-#include "api/datatransfermodel.h"
-#include "api/lrc.h"
 #include "api/newaccountmodel.h"
 #include "api/newcallmodel.h"
-#include "api/newcodecmodel.h"
-#include "api/newdevicemodel.h"
-#include "api/peerdiscoverymodel.h"
 
-#include <QBuffer>
-#include <QMutex>
 #include <QObject>
-#include <QPixmap>
-#include <QRegularExpression>
-#include <QSettings>
-#include <QtConcurrent/QtConcurrent>
 
 #include <memory>
 
@@ -67,369 +56,65 @@ class LRCInstance : public QObject
     Q_OBJECT
 
 public:
-    LRCInstance(migrateCallback willMigrateCb = {},
-                migrateCallback didMigrateCb = {},
-                const QString& updateUrl = {},
-                ConnectivityMonitor* connectivityMonitor = {})
-    {
-        lrc_ = std::make_unique<Lrc>(willMigrateCb, didMigrateCb);
-        renderer_ = std::make_unique<RenderManager>(lrc_->getAVModel());
-        updateManager_ = std::make_unique<UpdateManager>(updateUrl, connectivityMonitor, this);
-    };
-
-    Lrc& getAPI()
-    {
-        return *(lrc_);
-    }
-
-    RenderManager* renderer()
-    {
-        return renderer_.get();
-    }
-
-    UpdateManager* getUpdateManager()
-    {
-        return updateManager_.get();
-    }
-
-    void connectivityChanged()
-    {
-        lrc_->connectivityChanged();
-    }
-
-    NewAccountModel& accountModel()
-    {
-        return lrc_->getAccountModel();
-    }
-
-    BehaviorController& behaviorController()
-    {
-        return lrc_->getBehaviorController();
-    }
-
-    DataTransferModel& dataTransferModel()
-    {
-        return lrc_->getDataTransferModel();
-    }
-
-    AVModel& avModel()
-    {
-        return lrc_->getAVModel();
-    }
-
-    PluginModel& pluginModel()
-    {
-        return lrc_->getPluginModel();
-    }
-
-    bool isConnected()
-    {
-        return lrc_->isConnected();
-    }
-
-    VectorString getActiveCalls()
-    {
-        return lrc_->activeCalls();
-    }
-
-    const account::Info& getAccountInfo(const QString& accountId)
-    {
-        return accountModel().getAccountInfo(accountId);
-    }
-
-    const account::Info& getCurrentAccountInfo()
-    {
-        return getAccountInfo(getCurrAccId());
-    }
-
-    bool hasVideoCall()
-    {
-        auto activeCalls = lrc_->activeCalls();
-        auto accountList = accountModel().getAccountList();
-        bool result = false;
-        for (const auto& callId : activeCalls) {
-            for (const auto& accountId : accountList) {
-                auto& accountInfo = accountModel().getAccountInfo(accountId);
-                if (accountInfo.callModel->hasCall(callId)) {
-                    auto call = accountInfo.callModel->getCall(callId);
-                    result |= !(call.isAudioOnly || call.videoMuted);
-                }
-            }
-        }
-        return result;
-    }
-
-    QString getCallIdForConversationUid(const QString& convUid, const QString& accountId)
-    {
-        const auto& convInfo = getConversationFromConvUid(convUid, accountId);
-        if (convInfo.uid.isEmpty()) {
-            return {};
-        }
-        return convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId;
-    }
-
-    const call::Info* getCallInfo(const QString& callId, const QString& accountId)
-    {
-        try {
-            auto& accInfo = accountModel().getAccountInfo(accountId);
-            if (!accInfo.callModel->hasCall(callId)) {
-                return nullptr;
-            }
-            return &accInfo.callModel->getCall(callId);
-        } catch (...) {
-            return nullptr;
-        }
-    }
-
+    explicit LRCInstance(migrateCallback willMigrateCb = {},
+                         migrateCallback didMigrateCb = {},
+                         const QString& updateUrl = {},
+                         ConnectivityMonitor* connectivityMonitor = {});
+    ~LRCInstance() = default;
+
+    void finish();
+
+    RenderManager* renderer();
+    UpdateManager* getUpdateManager();
+
+    NewAccountModel& accountModel();
+    ConversationModel* getCurrentConversationModel();
+    NewCallModel* getCurrentCallModel();
+    AVModel& avModel();
+    PluginModel& pluginModel();
+    BehaviorController& behaviorController();
+    DataTransferModel& dataTransferModel();
+
+    void subscribeToDebugReceived();
+    bool isConnected();
+    void connectivityChanged();
+    VectorString getActiveCalls();
+
+    const account::Info& getAccountInfo(const QString& accountId);
+    const account::Info& getCurrentAccountInfo();
+    QString getCallIdForConversationUid(const QString& convUid, const QString& accountId);
+    const call::Info* getCallInfo(const QString& callId, const QString& accountId);
     const call::Info* getCallInfoForConversation(const conversation::Info& convInfo,
-                                                 bool forceCallOnly = {})
-    {
-        try {
-            auto accountId = convInfo.accountId;
-            auto& accInfo = accountModel().getAccountInfo(accountId);
-            auto callId = forceCallOnly
-                              ? convInfo.callId
-                              : (convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId);
-            if (!accInfo.callModel->hasCall(callId)) {
-                return nullptr;
-            }
-            return &accInfo.callModel->getCall(callId);
-        } catch (...) {
-            return nullptr;
-        }
-    }
-
+                                                 bool forceCallOnly = {});
     const conversation::Info& getConversationFromConvUid(const QString& convUid,
-                                                         const QString& accountId = {})
-    {
-        auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId
-                                                                           : getCurrAccId());
-        auto& convModel = accInfo.conversationModel;
-        return convModel->getConversationForUid(convUid).value_or(invalid);
-    }
-
+                                                         const QString& accountId = {});
     const conversation::Info& getConversationFromPeerUri(const QString& peerUri,
-                                                         const QString& accountId = {})
-    {
-        auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId
-                                                                           : getCurrAccId());
-        auto& convModel = accInfo.conversationModel;
-        return convModel->getConversationForPeerUri(peerUri).value_or(invalid);
-    }
-
+                                                         const QString& accountId = {});
     const conversation::Info& getConversationFromCallId(const QString& callId,
-                                                        const QString& accountId = {})
-    {
-        auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId
-                                                                           : getCurrAccId());
-        auto& convModel = accInfo.conversationModel;
-        return convModel->getConversationForCallId(callId).value_or(invalid);
-    }
-
-    ConversationModel* getCurrentConversationModel()
-    {
-        return getCurrentAccountInfo().conversationModel.get();
-    }
-
-    NewCallModel* getCurrentCallModel()
-    {
-        return getCurrentAccountInfo().callModel.get();
-    }
-
-    const QString& getCurrAccId()
-    {
-        if (selectedAccountId_.isEmpty()) {
-            auto accountList = accountModel().getAccountList();
-            if (accountList.size())
-                selectedAccountId_ = accountList.at(0);
-        }
-        return selectedAccountId_;
-    }
-
-    void setSelectedAccountId(const QString& accountId = {})
-    {
-        if (accountId == selectedAccountId_)
-            return; // No need to select current selected account
-
-        selectedAccountId_ = accountId;
-
-        // Last selected account should be set as preferred.
-        accountModel().setTopAccount(accountId);
-
-        Q_EMIT currentAccountChanged();
-    }
-
-    const QString& getCurrentConvUid()
-    {
-        return selectedConvUid_;
-    }
-
-    void setSelectedConvId(const QString& convUid = {})
-    {
-        selectedConvUid_ = convUid;
-    }
-
-    void reset(bool newInstance = false)
-    {
-        if (newInstance) {
-            renderer_.reset(new RenderManager(avModel()));
-            lrc_.reset(new Lrc());
-        } else {
-            renderer_.reset();
-            lrc_.reset();
-        }
-    }
-
-    int getCurrentAccountIndex()
-    {
-        for (int i = 0; i < accountModel().getAccountList().size(); i++) {
-            if (accountModel().getAccountList()[i] == getCurrAccId()) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    void setAvatarForAccount(const QPixmap& avatarPixmap, const QString& accountID)
-    {
-        QByteArray ba;
-        QBuffer bu(&ba);
-        bu.open(QIODevice::WriteOnly);
-        avatarPixmap.save(&bu, "PNG");
-        auto str = QString::fromLocal8Bit(ba.toBase64());
-        accountModel().setAvatar(accountID, str);
-    }
-
-    void setCurrAccAvatar(const QPixmap& avatarPixmap)
-    {
-        QByteArray ba;
-        QBuffer bu(&ba);
-        bu.open(QIODevice::WriteOnly);
-        avatarPixmap.save(&bu, "PNG");
-        auto str = QString::fromLocal8Bit(ba.toBase64());
-        accountModel().setAvatar(getCurrAccId(), str);
-    }
-
-    void setCurrAccAvatar(const QString& avatar)
-    {
-        accountModel().setAvatar(getCurrAccId(), avatar);
-    }
-
-    void setCurrAccDisplayName(const QString& displayName)
-    {
-        auto accountId = getCurrAccId();
-        accountModel().setAlias(accountId, displayName);
-        /*
-         * Force save to .yml.
-         */
-        auto confProps = accountModel().getAccountConfig(accountId);
-        accountModel().setAccountConfig(accountId, confProps);
-    }
-
-    const account::ConfProperties_t& getCurrAccConfig()
-    {
-        return getCurrentAccountInfo().confProperties;
-    }
-
-    void subscribeToDebugReceived()
-    {
-        lrc_->subscribeToDebugReceived();
-    }
-
-    void startAudioMeter(bool async)
-    {
-        auto f = [this] {
-            if (!getActiveCalls().size()) {
-                avModel().startAudioDevice();
-            }
-            avModel().setAudioMeterState(true);
-        };
-        if (async) {
-            QtConcurrent::run(f);
-        } else {
-            f();
-        }
-    }
-
-    void stopAudioMeter(bool async)
-    {
-        auto f = [this] {
-            if (!getActiveCalls().size()) {
-                avModel().stopAudioDevice();
-            }
-            avModel().setAudioMeterState(false);
-        };
-        if (async) {
-            QtConcurrent::run(f);
-        } else {
-            f();
-        }
-    }
-
-    QString getContentDraft(const QString& convUid, const QString& accountId)
-    {
-        auto draftKey = accountId + "_" + convUid;
-        return contentDrafts_[draftKey];
-    }
-
-    void setContentDraft(const QString& convUid, const QString& accountId, const QString& content)
-    {
-        auto draftKey = accountId + "_" + convUid;
-        contentDrafts_[draftKey] = content;
-    }
-
-    void pushlastConference(const QString& confId, const QString& callId)
-    {
-        lastConferences_[confId] = callId;
-    }
-
-    QString poplastConference(const QString& confId)
-    {
-        QString callId = {};
-        auto iter = lastConferences_.find(confId);
-        if (iter != lastConferences_.end()) {
-            callId = iter.value();
-            lastConferences_.erase(iter);
-        }
-        return callId;
-    }
-
-    void selectConversation(const QString& accountId, const QString& convUid)
-    {
-        const auto& convInfo = getConversationFromConvUid(convUid, accountId);
-
-        if (getCurrentConvUid() != convInfo.uid || convInfo.participants.size() > 0) {
-            // If the account is not currently selected, do that first, then
-            // proceed to select the conversation.
-            auto selectConversation = [this, accountId, convUid = convInfo.uid] {
-                const auto& convInfo = getConversationFromConvUid(convUid, accountId);
-                if (convInfo.uid.isEmpty()) {
-                    return;
-                }
-                auto& accInfo = getAccountInfo(convInfo.accountId);
-                setSelectedConvId(convInfo.uid);
-                accInfo.conversationModel->clearUnreadInteractions(convInfo.uid);
-
-                try {
-                    // Set contact filter (for conversation tab selection)
-                    auto& contact = accInfo.contactModel->getContact(convInfo.participants.front());
-                    setProperty("currentTypeFilter", QVariant::fromValue(contact.profileInfo.type));
-                } catch (const std::out_of_range& e) {
-                    qDebug() << e.what();
-                }
-            };
-            if (convInfo.accountId != getCurrAccId()) {
-                Utils::oneShotConnect(this,
-                                      &LRCInstance::currentAccountChanged,
-                                      [selectConversation] { selectConversation(); });
-                setSelectedConvId();
-                setSelectedAccountId(convInfo.accountId);
-            } else {
-                selectConversation();
-            }
-        }
-        Q_EMIT conversationSelected();
-    }
+                                                        const QString& accountId = {});
+
+    const QString& getCurrAccId();
+    void setSelectedAccountId(const QString& accountId = {});
+    const QString& getCurrentConvUid();
+    void selectConversation(const QString& accountId, const QString& convUid);
+    void setSelectedConvId(const QString& convUid = {});
+    int getCurrentAccountIndex();
+    void setAvatarForAccount(const QPixmap& avatarPixmap, const QString& accountID);
+    void setCurrAccAvatar(const QPixmap& avatarPixmap);
+    void setCurrAccAvatar(const QString& avatar);
+    void setCurrAccDisplayName(const QString& displayName);
+    const account::ConfProperties_t& getCurrAccConfig();
+
+    void startAudioMeter(bool async);
+    void stopAudioMeter(bool async);
+
+    QString getContentDraft(const QString& convUid, const QString& accountId);
+    void setContentDraft(const QString& convUid, const QString& accountId, const QString& content);
+
+    bool hasVideoCall();
+    void pushlastConference(const QString& confId, const QString& callId);
+    QString poplastConference(const QString& confId);
+    VectorString getConferenceSubcalls(const QString& callId);
 
 Q_SIGNALS:
     void accountListChanged();
diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp
index 76e22e9cef4c4f260f34bffb14ba9c91997d81b9..7a7aebe69d17503a46a4ce0f4bf5d38df6917938 100644
--- a/src/mainapplication.cpp
+++ b/src/mainapplication.cpp
@@ -46,6 +46,9 @@
 #include <QFontDatabase>
 #include <QMenu>
 #include <QQmlContext>
+#include <QResource>
+#include <QTranslator>
+#include <QLibraryInfo>
 
 #include <locale.h>
 #include <thread>
@@ -339,7 +342,6 @@ MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm)
         downloadUrl,
         cm));
     lrcInstance_->subscribeToDebugReceived();
-    lrcInstance_->getAPI().holdConferences = false;
 }
 
 const QVariantMap
diff --git a/src/mediacodeclistmodel.cpp b/src/mediacodeclistmodel.cpp
index 1887fba61051d2eb742ffcf39db94a15f824bf3d..fb4cec22a3d08a825ccf9bfb1ca52ac0fda6fde8 100644
--- a/src/mediacodeclistmodel.cpp
+++ b/src/mediacodeclistmodel.cpp
@@ -23,6 +23,7 @@
 #include "api/account.h"
 #include "api/contact.h"
 #include "api/conversation.h"
+#include "api/newcodecmodel.h"
 #include "api/newdevicemodel.h"
 
 MediaCodecListModel::MediaCodecListModel(QObject* parent)
diff --git a/src/messagesadapter.cpp b/src/messagesadapter.cpp
index 1cdf09ee5aceb301366f75b2322d36d066c0a003..a5c6db97b04834b6d4fb1fe3e03b8aa19e445958 100644
--- a/src/messagesadapter.cpp
+++ b/src/messagesadapter.cpp
@@ -35,6 +35,8 @@
 #include <QImageReader>
 #include <QList>
 #include <QUrl>
+#include <QMimeData>
+#include <QBuffer>
 
 MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager,
                                  LRCInstance* instance,
diff --git a/src/pluginlistpreferencemodel.h b/src/pluginlistpreferencemodel.h
index 1ce6f6e0677c03eb750eab120b49012bb05fdf7a..614eb4284508e2136cdb04d57ae0f963567f3b2b 100644
--- a/src/pluginlistpreferencemodel.h
+++ b/src/pluginlistpreferencemodel.h
@@ -20,6 +20,8 @@
 
 #include "abstractitemmodelbase.h"
 
+#include "api/pluginmodel.h"
+
 class PluginListPreferenceModel : public AbstractListModelBase
 {
     Q_OBJECT
diff --git a/src/qmlregister.cpp b/src/qmlregister.cpp
index 78adb0ce6433c8715d8db06242838572c7c93795..47a2d06bbab739ba5602cbc30cd5f8ec3518236f 100644
--- a/src/qmlregister.cpp
+++ b/src/qmlregister.cpp
@@ -39,6 +39,12 @@
 #include "videoformatresolutionmodel.h"
 #include "videoinputdevicemodel.h"
 
+#include "api/peerdiscoverymodel.h"
+#include "api/newcodecmodel.h"
+#include "api/newdevicemodel.h"
+#include "api/datatransfermodel.h"
+#include "api/pluginmodel.h"
+
 #include <QMetaType>
 #include <QQmlEngine>
 
diff --git a/src/settingsadapter.cpp b/src/settingsadapter.cpp
index a2ea168b68431f5fca3fc86e22d982e4a35b58d4..8f770c31df9db8996bc50063d69a83d04988e5fd 100644
--- a/src/settingsadapter.cpp
+++ b/src/settingsadapter.cpp
@@ -18,6 +18,7 @@
 
 #include "settingsadapter.h"
 
+#include "api/newcodecmodel.h"
 #include "api/newdevicemodel.h"
 
 SettingsAdapter::SettingsAdapter(AppSettingsManager* settingsManager,
diff --git a/src/updatemanager.cpp b/src/updatemanager.cpp
index 68f98a6abd86174f760b698a239f75be6cc0f402..1d5b3e9234bcfb51c7cf7c5948c6c75fe5b493d3 100644
--- a/src/updatemanager.cpp
+++ b/src/updatemanager.cpp
@@ -128,7 +128,7 @@ UpdateManager::applyUpdates(bool beta)
     get(
         downloadUrl,
         [this, downloadUrl](const QString&) {
-            lrcInstance_->reset();
+            lrcInstance_->finish();
             Q_EMIT lrcInstance_->quitEngineRequested();
             auto args = QString(" /passive /norestart WIXNONUILAUNCH=1");
             QProcess process;
diff --git a/src/utilsadapter.cpp b/src/utilsadapter.cpp
index dafd36a0901a660a7258973e66a61bd7c825ef34..3b983086104ea150a8e74d9d026024092bee43aa 100644
--- a/src/utilsadapter.cpp
+++ b/src/utilsadapter.cpp
@@ -27,6 +27,8 @@
 #include "utils.h"
 #include "version.h"
 
+#include "api/pluginmodel.h"
+
 #include <QApplication>
 #include <QClipboard>
 #include <QFileInfo>