From eb53a622b7f51160a2204fefa9c6a2e019bd9aff Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Thu, 3 Sep 2020 09:24:49 -0400
Subject: [PATCH] qml interop: start replacing QMetaObject::invokeMethod with
 signals

It seems concurrent calls to invokeMethod using qml component
object pointers can lead to access violations. These method
invocations can be replaced with a signal/slot mechanism.

This patch replaces only the invocations in conversationsadapter
and accountsadapter that are known to be problematic for now.

Some code cleanup is done for QmlAdapterBase derived classes.

Gitlab: #61
Change-Id: I72f47c9229a9bf42299ae52822c3a1a8c04eb423
---
 jami-qt.pro                                   |   1 -
 src/accountadapter.cpp                        |   6 +-
 src/accountadapter.h                          |  11 +-
 src/avadapter.cpp                             |   6 -
 src/avadapter.h                               |   9 +-
 src/calladapter.cpp                           |   8 +-
 src/calladapter.h                             |  11 +-
 src/contactadapter.cpp                        |   8 +-
 src/contactadapter.h                          |  13 ++-
 src/conversationsadapter.cpp                  |  22 ++--
 src/conversationsadapter.h                    |  17 ++-
 src/mainview/MainView.qml                     |   4 -
 src/mainview/components/AccountComboBox.qml   |  14 ++-
 .../components/ConversationSmartListView.qml  | 105 +++++++++---------
 src/mainview/components/SidePanel.qml         |   2 +-
 src/mediahandleradapter.cpp                   |   6 -
 src/mediahandleradapter.h                     |  14 +--
 src/messagesadapter.cpp                       |  10 +-
 src/messagesadapter.h                         |  11 +-
 src/qmladapterbase.cpp                        |  35 ------
 src/qmladapterbase.h                          |  17 ++-
 src/qmlregister.cpp                           |   2 +-
 22 files changed, 144 insertions(+), 188 deletions(-)
 delete mode 100644 src/qmladapterbase.cpp

diff --git a/jami-qt.pro b/jami-qt.pro
index 84817dc65..6ac56315f 100644
--- a/jami-qt.pro
+++ b/jami-qt.pro
@@ -174,7 +174,6 @@ SOURCES += ./src/bannedlistmodel.cpp \
         ./src/conversationsadapter.cpp \
         ./src/distantrenderer.cpp \
         ./src/previewrenderer.cpp \
-        ./src/qmladapterbase.cpp \
         ./src/avadapter.cpp \
         ./src/contactadapter.cpp \
         ./src/mediahandleradapter.cpp \
diff --git a/src/accountadapter.cpp b/src/accountadapter.cpp
index 00923de5e..d924bff90 100644
--- a/src/accountadapter.cpp
+++ b/src/accountadapter.cpp
@@ -30,8 +30,6 @@ AccountAdapter::AccountAdapter(QObject *parent)
     : QmlAdapterBase(parent)
 {}
 
-AccountAdapter::~AccountAdapter() {}
-
 AccountAdapter &
 AccountAdapter::instance()
 {
@@ -40,7 +38,7 @@ AccountAdapter::instance()
 }
 
 void
-AccountAdapter::initQmlObject()
+AccountAdapter::safeInit()
 {
     setSelectedAccount(LRCInstance::getCurrAccId());
 }
@@ -334,7 +332,7 @@ void
 AccountAdapter::backToWelcomePage()
 {
     deselectConversation();
-    QMetaObject::invokeMethod(qmlObj_, "backToWelcomePage");
+    emit navigateToWelcomePageRequested();
 }
 
 void
diff --git a/src/accountadapter.h b/src/accountadapter.h
index d2e9844d7..e28cec893 100644
--- a/src/accountadapter.h
+++ b/src/accountadapter.h
@@ -27,13 +27,18 @@
 #include "lrcinstance.h"
 #include "utils.h"
 
-class AccountAdapter : public QmlAdapterBase
+class AccountAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
 
 public:
     explicit AccountAdapter(QObject *parent = 0);
-    ~AccountAdapter();
+    ~AccountAdapter() = default;
+
+protected:
+    void safeInit() override;
+
+public:
     //Singleton
     static AccountAdapter &instance();
 
@@ -94,9 +99,9 @@ signals:
      */
     void reportFailure();
     void accountAdded(bool showBackUp, int index);
+    void navigateToWelcomePageRequested();
 
 private:
-    void initQmlObject() override final;
     void setSelectedAccount(const QString &accountId);
     void backToWelcomePage();
     void deselectConversation();
diff --git a/src/avadapter.cpp b/src/avadapter.cpp
index b7a256449..5306fda3f 100644
--- a/src/avadapter.cpp
+++ b/src/avadapter.cpp
@@ -28,12 +28,6 @@ AvAdapter::AvAdapter(QObject *parent)
     : QmlAdapterBase(parent)
 {}
 
-AvAdapter::~AvAdapter() {}
-
-void
-AvAdapter::initQmlObject()
-{}
-
 QVariantMap
 AvAdapter::populateVideoDeviceContextMenuItem()
 {
diff --git a/src/avadapter.h b/src/avadapter.h
index 1c300e883..ec64067af 100644
--- a/src/avadapter.h
+++ b/src/avadapter.h
@@ -24,13 +24,16 @@
 #include <QVariant>
 #include <QString>
 
-class AvAdapter : public QmlAdapterBase
+class AvAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
 
 public:
     explicit AvAdapter(QObject *parent = nullptr);
-    ~AvAdapter();
+    ~AvAdapter() = default;
+
+protected:
+    void safeInit() override {};
 
     /*
      * Return needed info for populating video device context menu item.
@@ -62,6 +65,4 @@ public:
      */
     Q_INVOKABLE void shareScreenArea(int screenNumber, int x, int y, int width, int height);
 
-private:
-    void initQmlObject() override;
 };
diff --git a/src/calladapter.cpp b/src/calladapter.cpp
index 962a5a783..098efacd1 100644
--- a/src/calladapter.cpp
+++ b/src/calladapter.cpp
@@ -30,12 +30,6 @@
 CallAdapter::CallAdapter(QObject* parent)
     : QmlAdapterBase(parent)
     , oneSecondTimer_(new QTimer(this))
-{}
-
-CallAdapter::~CallAdapter() {}
-
-void
-CallAdapter::initQmlObject()
 {
     connectCallModel(LRCInstance::getCurrAccId());
 
@@ -676,4 +670,4 @@ CallAdapter::setTime(const QString& accountId, const QString& convUid)
         auto timeString = LRCInstance::getCurrentCallModel()->getFormattedCallDuration(callId);
         emit updateTimeText(timeString);
     }
-}
\ No newline at end of file
+}
diff --git a/src/calladapter.h b/src/calladapter.h
index bd7871d79..d39d8427c 100644
--- a/src/calladapter.h
+++ b/src/calladapter.h
@@ -26,19 +26,18 @@
 #include <QString>
 #include <QVariant>
 
-class CallAdapter : public QmlAdapterBase
+class CallAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
 
 public:
     explicit CallAdapter(QObject* parent = nullptr);
-    ~CallAdapter();
+    ~CallAdapter() = default;
 
-    /*
-     * This is needed to be public since it has to be recognized by qml.
-     */
-    Q_INVOKABLE void initQmlObject() override;
+protected:
+    void safeInit() override {};
 
+public:
     Q_INVOKABLE void placeAudioOnlyCall();
     Q_INVOKABLE void placeCall();
     Q_INVOKABLE void hangUpACall(const QString& accountId, const QString& convUid);
diff --git a/src/contactadapter.cpp b/src/contactadapter.cpp
index e08a6dee8..9514d852c 100644
--- a/src/contactadapter.cpp
+++ b/src/contactadapter.cpp
@@ -1,4 +1,4 @@
-/*
+/*!
  * Copyright (C) 2020 by Savoir-faire Linux
  * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
  * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
@@ -28,8 +28,6 @@ ContactAdapter::ContactAdapter(QObject *parent)
     selectableProxyModel_.reset(new SelectableProxyModel(smartListModel_.get()));
 }
 
-ContactAdapter::~ContactAdapter() {}
-
 QVariant
 ContactAdapter::getContactSelectableModel(int type)
 {
@@ -175,7 +173,3 @@ ContactAdapter::setCalleeDisplayName(const QString &name)
 {
     calleeDisplayName_ = name;
 }
-
-void
-ContactAdapter::initQmlObject()
-{}
diff --git a/src/contactadapter.h b/src/contactadapter.h
index 4b694770d..9f3aae432 100644
--- a/src/contactadapter.h
+++ b/src/contactadapter.h
@@ -1,4 +1,4 @@
-/*
+/*!
  * Copyright (C) 2020 by Savoir-faire Linux
  * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
  *
@@ -34,7 +34,7 @@
  *
  * Additionally, user need to setFilterRegExp to be able to get input QRegExp from FilterPredicate.
  */
-class SelectableProxyModel : public QSortFilterProxyModel
+class SelectableProxyModel final : public QSortFilterProxyModel
 {
 public:
     using FilterPredicate = std::function<bool(const QModelIndex &, const QRegExp &)>;
@@ -68,13 +68,16 @@ private:
     std::function<bool(const QModelIndex &, const QRegExp &)> filterPredicate_;
 };
 
-class ContactAdapter : public QmlAdapterBase
+class ContactAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
 
 public:
     explicit ContactAdapter(QObject *parent = nullptr);
-    ~ContactAdapter();
+    ~ContactAdapter() = default;
+
+protected:
+    void safeInit() override {};
 
     Q_INVOKABLE QVariant getContactSelectableModel(int type);
     Q_INVOKABLE void setSearchFilter(const QString &filter);
@@ -82,8 +85,6 @@ public:
     Q_INVOKABLE void setCalleeDisplayName(const QString &name);
 
 private:
-    void initQmlObject() override;
-
     SmartListModel::Type listModeltype_;
 
     /*
diff --git a/src/conversationsadapter.cpp b/src/conversationsadapter.cpp
index 69d46f919..1c979a547 100644
--- a/src/conversationsadapter.cpp
+++ b/src/conversationsadapter.cpp
@@ -1,4 +1,4 @@
-/*
+/*!
  * Copyright (C) 2020 by Savoir-faire Linux
  * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
  * Author: Anthony L�onard <anthony.leonard@savoirfairelinux.com>
@@ -29,15 +29,12 @@ ConversationsAdapter::ConversationsAdapter(QObject *parent)
     : QmlAdapterBase(parent)
 {}
 
-ConversationsAdapter::~ConversationsAdapter() {}
-
 void
-ConversationsAdapter::initQmlObject()
+ConversationsAdapter::safeInit()
 {
     conversationSmartListModel_ = new SmartListModel(LRCInstance::getCurrAccId(), this);
 
-    QMetaObject::invokeMethod(qmlObj_, "setModel",
-                              Q_ARG(QVariant, QVariant::fromValue(conversationSmartListModel_)));
+    emit modelChanged(QVariant::fromValue(conversationSmartListModel_));
 
     connect(&LRCInstance::behaviorController(),
             &BehaviorController::showChatView,
@@ -57,7 +54,7 @@ void
 ConversationsAdapter::backToWelcomePage()
 {
     deselectConversation();
-    QMetaObject::invokeMethod(qmlObj_, "backToWelcomePage");
+    emit navigateToWelcomePageRequested();
 }
 
 void
@@ -198,7 +195,7 @@ ConversationsAdapter::connectConversationModel(bool updateFilter)
                                               [this]() {
         conversationSmartListModel_->fillConversationsList();
         updateConversationsFilterWidget();
-        QMetaObject::invokeMethod(qmlObj_, "updateConversationSmartListView");
+        emit updateListViewRequested();
         auto* convModel = LRCInstance::getCurrentConversationModel();
         const auto conversation = convModel->getConversationForUID(LRCInstance::getCurrentConvUid());
 
@@ -211,7 +208,7 @@ ConversationsAdapter::connectConversationModel(bool updateFilter)
                    == lrc::api::profile::Type::TEMPORARY) {
             return;
         }
-        QMetaObject::invokeMethod(qmlObj_, "modelSorted", Q_ARG(QVariant, contactURI));
+        emit modelSorted(QVariant::fromValue(contactURI));
     });
 
     modelUpdatedConnection_ = QObject::connect(currentConversationModel,
@@ -219,7 +216,7 @@ ConversationsAdapter::connectConversationModel(bool updateFilter)
                                                [this](const QString &convUid) {
         conversationSmartListModel_->updateConversation(convUid);
         updateConversationsFilterWidget();
-        QMetaObject::invokeMethod(qmlObj_, "updateConversationSmartListView");
+        emit updateListViewRequested();
     });
 
     filterChangedConnection_ = QObject::connect(currentConversationModel,
@@ -228,7 +225,7 @@ ConversationsAdapter::connectConversationModel(bool updateFilter)
         conversationSmartListModel_->fillConversationsList();
         conversationSmartListModel_->setAccount(LRCInstance::getCurrAccId());
         updateConversationsFilterWidget();
-        QMetaObject::invokeMethod(qmlObj_, "updateConversationSmartListView");
+        emit updateListViewRequested();
     });
 
     newConversationConnection_ = QObject::connect(currentConversationModel,
@@ -265,9 +262,10 @@ ConversationsAdapter::connectConversationModel(bool updateFilter)
     searchResultUpdatedConnection_ = QObject::connect(currentConversationModel,
                                                       &lrc::api::ConversationModel::searchResultUpdated,
                                                       [this]() {
+
         conversationSmartListModel_->fillConversationsList();
         conversationSmartListModel_->setAccount(LRCInstance::getCurrAccId());
-        QMetaObject::invokeMethod(qmlObj_, "updateConversationSmartListView");
+        emit updateListViewRequested();
     });
 
     if (updateFilter) currentConversationModel->setFilter("");
diff --git a/src/conversationsadapter.h b/src/conversationsadapter.h
index 19cad97f4..56f346268 100644
--- a/src/conversationsadapter.h
+++ b/src/conversationsadapter.h
@@ -1,4 +1,4 @@
-/*
+/*!
  * Copyright (C) 2020 by Savoir-faire Linux
  * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
  *
@@ -25,14 +25,17 @@
 #include <QObject>
 #include <QString>
 
-class ConversationsAdapter : public QmlAdapterBase
+class ConversationsAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
-
 public:
     explicit ConversationsAdapter(QObject *parent = nullptr);
-    ~ConversationsAdapter();
+    ~ConversationsAdapter() = default;
+
+protected:
+    void safeInit() override;
 
+public:
     Q_INVOKABLE bool connectConversationModel(bool updateFilter = true);
     Q_INVOKABLE void disconnectConversationModel();
     Q_INVOKABLE void selectConversation(const QString &accountId,
@@ -50,8 +53,12 @@ signals:
     void showConversationTabs(bool visible);
     void showSearchStatus(const QString &status);
 
+    void modelChanged(const QVariant& model);
+    void modelSorted(const QVariant& uri);
+    void updateListViewRequested();
+    void navigateToWelcomePageRequested();
+
 private:
-    void initQmlObject() override;
     void setConversationFilter(lrc::api::profile::Type filter);
     void backToWelcomePage();
     bool selectConversation(const lrc::api::conversation::Info &item,
diff --git a/src/mainview/MainView.qml b/src/mainview/MainView.qml
index 1ab5a3fc0..bfd2f7f66 100644
--- a/src/mainview/MainView.qml
+++ b/src/mainview/MainView.qml
@@ -704,10 +704,6 @@ Window {
         height: userProfile.contentHeight
     }
 
-    Component.onCompleted: {
-        CallAdapter.initQmlObject()
-    }
-
     onClosing: {
         close.accepted = false
         mainViewWindow.hide()
diff --git a/src/mainview/components/AccountComboBox.qml b/src/mainview/components/AccountComboBox.qml
index 0252b8d7f..b1bc5029f 100644
--- a/src/mainview/components/AccountComboBox.qml
+++ b/src/mainview/components/AccountComboBox.qml
@@ -27,14 +27,10 @@ ComboBox {
     id: accountComboBox
 
     signal accountChanged(int index)
-    signal needToBackToWelcomePage()
+    signal needToBackToWelcomePage
     signal newAccountButtonClicked
     signal settingBtnClicked
 
-    function backToWelcomePage() {
-        needToBackToWelcomePage()
-    }
-
     // Reset accountListModel.
     function resetAccountListModel() {
         accountListModel.reset()
@@ -53,6 +49,14 @@ ComboBox {
         }
     }
 
+    Connections {
+        target: ClientWrapper.accountAdaptor
+
+        function onNavigateToWelcomePageRequested() {
+            needToBackToWelcomePage()
+        }
+    }
+
     Image {
         id: userImageRoot
 
diff --git a/src/mainview/components/ConversationSmartListView.qml b/src/mainview/components/ConversationSmartListView.qml
index f850c1173..1d61c5bb3 100644
--- a/src/mainview/components/ConversationSmartListView.qml
+++ b/src/mainview/components/ConversationSmartListView.qml
@@ -1,7 +1,7 @@
-
 /*
  * Copyright (C) 2020 by Savoir-faire Linux
  * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
+ * 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
@@ -16,13 +16,14 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
+
 import QtQuick 2.14
 import QtQuick.Controls 2.14
 import QtQuick.Layouts 1.14
 import net.jami.Models 1.0
 
 ListView {
-    id: conversationSmartListView
+    id: root
 
     signal needToAccessMessageWebView(string currentUserDisplayName, string currentUserAlias, string currentUID, bool callStackViewShouldShow, bool isAudioOnly, int callState)
     signal needToSelectItems(string conversationUid)
@@ -34,60 +35,59 @@ ListView {
     signal currentIndexIsChanged
     signal forceUpdatePotentialInvalidItem
 
+    // Refresh all items within the model.
+    function updateListView() {
+        root.model.dataChanged(
+                    root.model.index(0, 0),
+                    root.model.index(
+                    root.model.rowCount() - 1, 0))
+        root.forceUpdatePotentialInvalidItem()
+    }
 
-    /*
-     * When model is sorted, we need to reset to focus (currentIndex)
-     * to the real conversation that we focused.
-     */
-    function modelSorted(contactURIToCompare) {
-        var conversationSmartListViewModel = conversationSmartListView.model
-        conversationSmartListView.currentIndex = -1
-        updateConversationSmartListView()
-        for (var i = 0; i < count; i++) {
-            if (conversationSmartListViewModel.data(
-                        conversationSmartListViewModel.index(i, 0),
-                        261) === contactURIToCompare) {
-                conversationSmartListView.currentIndex = i
-                break
-            }
-        }
+    ConversationSmartListContextMenu {
+        id: smartListContextMenu
     }
 
+    Connections {
+        target: ConversationsAdapter
 
-    /*
-     * Refresh all item within model.
-     */
-    function updateConversationSmartListView() {
-        var conversationSmartListViewModel = conversationSmartListView.model
-        conversationSmartListViewModel.dataChanged(
-                    conversationSmartListViewModel.index(0, 0),
-                    conversationSmartListViewModel.index(
-                        conversationSmartListViewModel.rowCount() - 1, 0))
-        conversationSmartListView.forceUpdatePotentialInvalidItem()
-    }
+        function onModelChanged(model) {
+            root.model = model
+        }
 
-    function setModel(model) {
-        conversationSmartListView.model = model
-    }
+        // When the model has been sorted, we need to adjust the focus (currentIndex)
+        // to the previously focused conversation item.
+        function onModelSorted(uri) {
+            root.currentIndex = -1
+            updateListView()
+            for (var i = 0; i < count; i++) {
+                if (root.model.data(
+                    root.model.index(i, 0), 261) === uri) {
+                    root.currentIndex = i
+                    break
+                }
+            }
+        }
 
-    function backToWelcomePage() {
-        conversationSmartListView.needToBackToWelcomePage()
-    }
+        function onUpdateListViewRequested() {
+            updateListView()
+        }
 
-    ConversationSmartListContextMenu {
-        id: smartListContextMenu
+        function onNavigateToWelcomePageRequested() {
+            root.needToBackToWelcomePage()
+        }
     }
 
     Connections {
         target: CallAdapter
 
         function onUpdateConversationSmartList() {
-            updateConversationSmartListView()
+            updateListView()
         }
     }
 
     onCurrentIndexChanged: {
-        conversationSmartListView.currentIndexIsChanged()
+        root.currentIndexIsChanged()
     }
 
     clip: true
@@ -101,7 +101,7 @@ ListView {
     Shortcut {
         sequence: "Ctrl+Shift+X"
         context: Qt.ApplicationShortcut
-        enabled: conversationSmartListView.visible
+        enabled: root.visible
         onActivated: {
             CallAdapter.placeCall()
         }
@@ -110,7 +110,7 @@ ListView {
     Shortcut {
         sequence: "Ctrl+Shift+C"
         context: Qt.ApplicationShortcut
-        enabled: conversationSmartListView.visible
+        enabled: root.visible
         onActivated: {
             CallAdapter.placeAudioOnlyCall()
         }
@@ -119,7 +119,7 @@ ListView {
     Shortcut {
         sequence: "Ctrl+Shift+L"
         context: Qt.ApplicationShortcut
-        enabled: conversationSmartListView.visible
+        enabled: root.visible
         onActivated: {
             ClientWrapper.utilsAdaptor.clearConversationHistory(ClientWrapper.utilsAdaptor.getCurrAccId(),
                                                   ClientWrapper.utilsAdaptor.getCurrConvId())
@@ -129,18 +129,18 @@ ListView {
     Shortcut {
         sequence: "Ctrl+Shift+B"
         context: Qt.ApplicationShortcut
-        enabled: conversationSmartListView.visible
+        enabled: root.visible
         onActivated: {
             ClientWrapper.utilsAdaptor.removeConversation(ClientWrapper.utilsAdaptor.getCurrAccId(),
                                             ClientWrapper.utilsAdaptor.getCurrConvId(), true)
-            conversationSmartListView.needToBackToWelcomePage()
+            root.needToBackToWelcomePage()
         }
     }
 
     Shortcut {
         sequence: "Ctrl+Shift+Delete"
         context: Qt.ApplicationShortcut
-        enabled: conversationSmartListView.visible
+        enabled: root.visible
         onActivated: {
             ClientWrapper.utilsAdaptor.removeConversation(ClientWrapper.utilsAdaptor.getCurrAccId(),
                                             ClientWrapper.utilsAdaptor.getCurrConvId(), false)
@@ -150,21 +150,22 @@ ListView {
     Shortcut {
         sequence: "Ctrl+Down"
         context: Qt.ApplicationShortcut
-        enabled: conversationSmartListView.visible
+        enabled: root.visible
         onActivated: {
-            if (currentIndex + 1 >= count) return
-            conversationSmartListView.currentIndex += 1
+            if (currentIndex + 1 >= count)
+                return
+            root.currentIndex += 1
         }
     }
 
     Shortcut {
         sequence: "Ctrl+Up"
         context: Qt.ApplicationShortcut
-        enabled: conversationSmartListView.visible
+        enabled: root.visible
         onActivated: {
-            if (currentIndex <= 0) return
-            conversationSmartListView.currentIndex -= 1
+            if (currentIndex <= 0)
+                return
+            root.currentIndex -= 1
         }
     }
-
 }
diff --git a/src/mainview/components/SidePanel.qml b/src/mainview/components/SidePanel.qml
index 01d13b5b5..bf2d6ffb7 100644
--- a/src/mainview/components/SidePanel.qml
+++ b/src/mainview/components/SidePanel.qml
@@ -91,7 +91,7 @@ Rectangle {
     }
 
     function forceUpdateConversationSmartListView() {
-        conversationSmartListView.updateConversationSmartListView()
+        conversationSmartListView.updateListView()
     }
 
     /*
diff --git a/src/mediahandleradapter.cpp b/src/mediahandleradapter.cpp
index 120a55e7d..d35117d59 100644
--- a/src/mediahandleradapter.cpp
+++ b/src/mediahandleradapter.cpp
@@ -24,8 +24,6 @@ MediaHandlerAdapter::MediaHandlerAdapter(QObject* parent)
     : QmlAdapterBase(parent)
 {}
 
-MediaHandlerAdapter::~MediaHandlerAdapter() {}
-
 QVariant
 MediaHandlerAdapter::getMediaHandlerSelectableModel()
 {
@@ -61,7 +59,3 @@ MediaHandlerAdapter::getMediaHandlerPreferencesSelectableModel(QString pluginId)
 
     return QVariant::fromValue(mediaHandlerListPreferenceModel_.get());
 }
-
-void
-MediaHandlerAdapter::initQmlObject()
-{}
diff --git a/src/mediahandleradapter.h b/src/mediahandleradapter.h
index 6681ada13..57837a68f 100644
--- a/src/mediahandleradapter.h
+++ b/src/mediahandleradapter.h
@@ -1,4 +1,4 @@
-/**
+/*!
  * Copyright (C) 2020 by Savoir-faire Linux
  * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
  *
@@ -19,7 +19,6 @@
 #pragma once
 
 #include "qmladapterbase.h"
-//#include "smartlistmodel.h"
 #include "mediahandleritemlistmodel.h"
 #include "mediahandlerlistpreferencemodel.h"
 #include "preferenceitemlistmodel.h"
@@ -28,22 +27,23 @@
 #include <QSortFilterProxyModel>
 #include <QString>
 
-class MediaHandlerAdapter : public QmlAdapterBase
+class MediaHandlerAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
-
 public:
     explicit MediaHandlerAdapter(QObject* parent = nullptr);
-    ~MediaHandlerAdapter();
+    ~MediaHandlerAdapter() = default;
+
+protected:
+    void safeInit() override {};
 
     Q_INVOKABLE QVariant getMediaHandlerSelectableModel();
     Q_INVOKABLE QVariant getMediaHandlerPreferencesModel(QString pluginId, QString mediaHandlerName);
     Q_INVOKABLE QVariant getMediaHandlerPreferencesSelectableModel(QString pluginId);
 
 private:
-    void initQmlObject();
-
     std::unique_ptr<MediaHandlerItemListModel> mediaHandlerListModel_;
     std::unique_ptr<PreferenceItemListModel> mediaHandlerPreferenceItemListModel_;
     std::unique_ptr<MediaHandlerListPreferenceModel> mediaHandlerListPreferenceModel_;
+
 };
diff --git a/src/messagesadapter.cpp b/src/messagesadapter.cpp
index f61c90ede..8b7cd94d0 100644
--- a/src/messagesadapter.cpp
+++ b/src/messagesadapter.cpp
@@ -1,7 +1,7 @@
-/*
+/*!
  * Copyright (C) 2020 by Savoir-faire Linux
  * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
- * Author: Anthony L�onard <anthony.leonard@savoirfairelinux.com>
+ * Author: Anthony Léonard <anthony.leonard@savoirfairelinux.com>
  * Author: Olivier Soldano <olivier.soldano@savoirfairelinux.com>
  * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
  * Author: Isa Nanic <isa.nanic@savoirfairelinux.com>
@@ -22,9 +22,9 @@
  */
 
 #include "messagesadapter.h"
-#include "webchathelpers.h"
 
 #include "utils.h"
+#include "webchathelpers.h"
 
 #include <QDesktopServices>
 #include <QFileInfo>
@@ -36,10 +36,8 @@ MessagesAdapter::MessagesAdapter(QObject *parent)
     : QmlAdapterBase(parent)
 {}
 
-MessagesAdapter::~MessagesAdapter() {}
-
 void
-MessagesAdapter::initQmlObject() {
+MessagesAdapter::safeInit() {
     connect(&LRCInstance::instance(),
             &LRCInstance::currentAccountChanged,
             [this](){
diff --git a/src/messagesadapter.h b/src/messagesadapter.h
index 352cc3eec..5f066a538 100644
--- a/src/messagesadapter.h
+++ b/src/messagesadapter.h
@@ -1,4 +1,4 @@
-/*
+/*!
  * Copyright (C) 2020 by Savoir-faire Linux
  * Author: Mingrui Zhang   <mingrui.zhang@savoirfairelinux.com>
  *
@@ -24,13 +24,15 @@
 #include <QObject>
 #include <QString>
 
-class MessagesAdapter : public QmlAdapterBase
+class MessagesAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
-
 public:
     explicit MessagesAdapter(QObject *parent = 0);
-    ~MessagesAdapter();
+    ~MessagesAdapter() = default;
+
+protected:
+    void safeInit() override;
 
     Q_INVOKABLE void setupChatView(const QString &uid);
     Q_INVOKABLE void connectConversationModel();
@@ -93,7 +95,6 @@ public slots:
     void slotMessagesLoaded();
 
 private:
-    void initQmlObject() override final;
     void setConversationProfileData(const lrc::api::conversation::Info &convInfo);
     void newInteraction(const QString &accountId,
                         const QString &convUid,
diff --git a/src/qmladapterbase.cpp b/src/qmladapterbase.cpp
deleted file mode 100644
index 6b8d3f68b..000000000
--- a/src/qmladapterbase.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2020 by Savoir-faire Linux
- * 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 "qmladapterbase.h"
-
-QmlAdapterBase::QmlAdapterBase(QObject *parent)
-    : QObject(parent)
-{
-    qmlObj_ = nullptr;
-}
-
-QmlAdapterBase::~QmlAdapterBase() {}
-
-void
-QmlAdapterBase::setQmlObject(QObject *obj)
-{
-    qmlObj_ = obj;
-
-    initQmlObject();
-}
diff --git a/src/qmladapterbase.h b/src/qmladapterbase.h
index e476359ea..750c09e60 100644
--- a/src/qmladapterbase.h
+++ b/src/qmladapterbase.h
@@ -28,20 +28,27 @@ class QmlAdapterBase : public QObject
 {
     Q_OBJECT
 public:
-    explicit QmlAdapterBase(QObject *parent = nullptr);
-    ~QmlAdapterBase();
+    explicit QmlAdapterBase(QObject *parent = nullptr)
+        : QObject(parent)
+        , qmlObj_(nullptr) {};
+
+    virtual ~QmlAdapterBase() = default;
 
     /*
      * This function should be called in the Component.onCompleted slot
      * in the qml component that this adapter should attach to.
      */
-    Q_INVOKABLE void setQmlObject(QObject *obj);
+    Q_INVOKABLE void setQmlObject(QObject *obj)
+    {
+        qmlObj_ = obj;
+        safeInit();
+    };
 
 protected:
     /*
-     *Once the qml object is set, custom actions can be done in this function.
+     * Once the qml object is set, Qml method invokation can be done
      */
-    virtual void initQmlObject() = 0;
+    virtual void safeInit() = 0;
 
     /*
      * Object pointer.
diff --git a/src/qmlregister.cpp b/src/qmlregister.cpp
index 11301adf1..02694a684 100644
--- a/src/qmlregister.cpp
+++ b/src/qmlregister.cpp
@@ -135,6 +135,7 @@ void registerTypes()
     QML_REGISTERSINGLETONTYPE_URL(QStringLiteral("qrc:/src/constant/JamiQmlUtils.qml"),
                                   JamiQmlUtils, 1, 0);
 
+    QML_REGISTERSINGLETONTYPE("net.jami.Models", AccountAdapter, 1, 0);
     QML_REGISTERSINGLETONTYPE("net.jami.Models", CallAdapter, 1, 0);
     QML_REGISTERSINGLETONTYPE("net.jami.Models", MessagesAdapter, 1, 0);
     QML_REGISTERSINGLETONTYPE("net.jami.Models", ConversationsAdapter, 1, 0);
@@ -167,7 +168,6 @@ void registerTypes()
      * qmlRegisterUncreatableType & Q_DECLARE_METATYPE to expose models in qml.
      */
     QML_REGISTERUNCREATABLE("net.jami.Models", RenderManager, 1, 0);
-    QML_REGISTERUNCREATABLE("net.jami.Models", AccountAdapter, 1, 0);
     QML_REGISTERUNCREATABLE("net.jami.Models", UtilsAdapter, 1, 0);
     QML_REGISTERUNCREATABLE("net.jami.Models", NameDirectory, 1, 0);
     QML_REGISTERUNCREATABLE("net.jami.Models", LRCInstance, 1, 0);
-- 
GitLab