From a5ec0c8e656f1c3ade439e666c0cc71cbca805b6 Mon Sep 17 00:00:00 2001
From: agsantos <aline.gondimsantos@savoirfairelinux.com>
Date: Fri, 22 Jan 2021 11:53:08 -0500
Subject: [PATCH] plugins: add ChatHandler API

Change-Id: I415fc953b9111ca7e3d899c1531a42bd43716ab2
---
 CMakeLists.txt                                |   4 +-
 jami-qt.pro                                   |   4 +-
 qml.qrc                                       |   6 +-
 src/constant/JamiTheme.qml                    |   2 +
 src/mainview/components/CallOverlay.qml       |  12 +-
 .../components/CallViewContextMenu.qml        |   2 +-
 .../components/MediaHandlerPicker.qml         | 259 ---------------
 src/mainview/components/MessageWebView.qml    |   9 +
 .../components/MessageWebViewHeader.qml       |  32 +-
 ...gate.qml => PluginHandlerItemDelegate.qml} |  16 +-
 .../components/PluginHandlerPicker.qml        | 297 ++++++++++++++++++
 src/mainview/components/RecordBox.qml         |   1 -
 src/mainview/js/mediahandlerpickercreation.js |  66 ----
 .../js/pluginhandlerpickercreation.js         |  78 +++++
 src/mediahandleritemlistmodel.cpp             | 149 ---------
 src/pluginadapter.cpp                         |  11 +-
 src/pluginadapter.h                           |  11 +-
 src/pluginhandleritemlistmodel.cpp            | 218 +++++++++++++
 ...stmodel.h => pluginhandleritemlistmodel.h} |  25 +-
 src/pluginitemlistmodel.cpp                   |   6 +-
 src/qmlregister.cpp                           |   4 +-
 .../components/PluginListPreferencesView.qml  |   4 +
 .../components/PluginListSettingsView.qml     |   1 +
 .../components/PluginSettingsPage.qml         |   1 +
 src/utilsadapter.cpp                          |  20 +-
 src/utilsadapter.h                            |   3 +-
 26 files changed, 718 insertions(+), 523 deletions(-)
 delete mode 100644 src/mainview/components/MediaHandlerPicker.qml
 rename src/mainview/components/{MediaHandlerItemDelegate.qml => PluginHandlerItemDelegate.qml} (89%)
 create mode 100644 src/mainview/components/PluginHandlerPicker.qml
 delete mode 100644 src/mainview/js/mediahandlerpickercreation.js
 create mode 100644 src/mainview/js/pluginhandlerpickercreation.js
 delete mode 100644 src/mediahandleritemlistmodel.cpp
 create mode 100644 src/pluginhandleritemlistmodel.cpp
 rename src/{mediahandleritemlistmodel.h => pluginhandleritemlistmodel.h} (73%)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a2b566b46..f6d0b3502 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,7 +66,7 @@ set(COMMON_SOURCES
     ${SRC_DIR}/settingsadapter.cpp
     ${SRC_DIR}/deviceitemlistmodel.cpp
     ${SRC_DIR}/pluginitemlistmodel.cpp
-    ${SRC_DIR}/mediahandleritemlistmodel.cpp
+    ${SRC_DIR}/pluginhandleritemlistmodel.cpp
     ${SRC_DIR}/preferenceitemlistmodel.cpp
     ${SRC_DIR}/mediacodeclistmodel.cpp
     ${SRC_DIR}/accountstomigratelistmodel.cpp
@@ -115,7 +115,7 @@ set(COMMON_HEADERS
     ${SRC_DIR}/settingsadapter.h
     ${SRC_DIR}/deviceitemlistmodel.h
     ${SRC_DIR}/pluginitemlistmodel.h
-    ${SRC_DIR}/mediahandleritemlistmodel.h
+    ${SRC_DIR}/pluginhandleritemlistmodel.h
     ${SRC_DIR}/preferenceitemlistmodel.h
     ${SRC_DIR}/mediacodeclistmodel.h
     ${SRC_DIR}/accountstomigratelistmodel.h
diff --git a/jami-qt.pro b/jami-qt.pro
index d57fd245f..9fc5d632a 100644
--- a/jami-qt.pro
+++ b/jami-qt.pro
@@ -170,7 +170,7 @@ HEADERS += \
         src/settingsadapter.h \
         src/deviceitemlistmodel.h \
         src/pluginitemlistmodel.h \
-        src/mediahandleritemlistmodel.h \
+        src/pluginhandleritemlistmodel.h \
         src/preferenceitemlistmodel.h \
         src/mediacodeclistmodel.h \
         src/accountstomigratelistmodel.h \
@@ -211,7 +211,7 @@ SOURCES += \
         src/settingsadapter.cpp \
         src/deviceitemlistmodel.cpp \
         src/pluginitemlistmodel.cpp \
-        src/mediahandleritemlistmodel.cpp \
+        src/pluginhandleritemlistmodel.cpp \
         src/preferenceitemlistmodel.cpp \
         src/mediacodeclistmodel.cpp \
         src/accountstomigratelistmodel.cpp \
diff --git a/qml.qrc b/qml.qrc
index 0181fbc43..b557894c6 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -45,7 +45,7 @@
         <file>src/commoncomponents/SettingParaCombobox.qml</file>
         <file>src/settingsview/components/DeviceItemDelegate.qml</file>
         <file>src/settingsview/components/PluginItemDelegate.qml</file>
-        <file>src/mainview/components/MediaHandlerItemDelegate.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>
@@ -112,9 +112,9 @@
         <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/MediaHandlerPicker.qml</file>
+        <file>src/mainview/components/PluginHandlerPicker.qml</file>
         <file>src/mainview/js/contactpickercreation.js</file>
-        <file>src/mainview/js/mediahandlerpickercreation.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>
diff --git a/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml
index f08d6852d..5bc49f020 100644
--- a/src/constant/JamiTheme.qml
+++ b/src/constant/JamiTheme.qml
@@ -170,6 +170,8 @@ Item {
     property int preferredDialogWidth: 400
     property int preferredDialogHeight: 300
     property int minimumPreviewWidth: 120
+    property int pluginHandlersPopupViewHeight: 200
+    property int pluginHandlersPopupViewDelegateHeight: 50
 
     // main application spec
     property int mainViewMinWidth: 460
diff --git a/src/mainview/components/CallOverlay.qml b/src/mainview/components/CallOverlay.qml
index d5fe292e7..fd0e9d7c0 100644
--- a/src/mainview/components/CallOverlay.qml
+++ b/src/mainview/components/CallOverlay.qml
@@ -28,7 +28,7 @@ import net.jami.Adapters 1.0
 import net.jami.Constants 1.0
 
 import "../js/contactpickercreation.js" as ContactPickerCreation
-import "../js/mediahandlerpickercreation.js" as MediaHandlerPickerCreation
+import "../js/pluginhandlerpickercreation.js" as PluginHandlerPickerCreation
 
 import "../../commoncomponents"
 
@@ -73,8 +73,8 @@ Rectangle {
         ContactPickerCreation.closeContactPicker()
     }
 
-    function closePotentialMediaHandlerPicker() {
-        MediaHandlerPickerCreation.closeMediaHandlerPicker()
+    function closePotentialPluginHandlerPicker() {
+        PluginHandlerPickerCreation.closePluginHandlerPicker()
     }
 
     // returns true if participant is not fully maximized
@@ -561,9 +561,9 @@ Rectangle {
         }
 
         onPluginItemClicked: {
-            // Create media handler picker - PLUGINS
-            MediaHandlerPickerCreation.createMediaHandlerPickerObjects(callOverlayRect)
-            MediaHandlerPickerCreation.openMediaHandlerPicker()
+            // Create plugin handler picker - PLUGINS
+            PluginHandlerPickerCreation.createPluginHandlerPickerObjects(callOverlayRect, true)
+            PluginHandlerPickerCreation.openPluginHandlerPicker()
         }
     }
 }
diff --git a/src/mainview/components/CallViewContextMenu.qml b/src/mainview/components/CallViewContextMenu.qml
index 75919c5cd..012fc9aad 100644
--- a/src/mainview/components/CallViewContextMenu.qml
+++ b/src/mainview/components/CallViewContextMenu.qml
@@ -131,7 +131,7 @@ Item {
                                              })
         }
 
-        if (UtilsAdapter.checkShowPluginsButton()) {
+        if (UtilsAdapter.checkShowPluginsButton(true)) {
             ContextMenuGenerator.addMenuItem(JamiStrings.viewPlugin,
                                              "qrc:/images/icons/extension_24dp.svg",
                                              function (){
diff --git a/src/mainview/components/MediaHandlerPicker.qml b/src/mainview/components/MediaHandlerPicker.qml
deleted file mode 100644
index bc7b8b6b5..000000000
--- a/src/mainview/components/MediaHandlerPicker.qml
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2020 by Savoir-faire Linux
- * Author: Aline Gondim Santos <aline.gondimsantos@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/>.
- */
-
-import QtQuick 2.14
-import QtQuick.Controls 2.14
-import QtQuick.Layouts 1.14
-import QtQuick.Controls.Universal 2.14
-import net.jami.Models 1.0
-import net.jami.Adapters 1.0
-import net.jami.Constants 1.0
-
-import "../../commoncomponents"
-
-Popup {
-    id: root
-    function toggleMediaHandlerSlot(mediaHandlerId, isLoaded) {
-        var callId = UtilsAdapter.getCallId(callStackViewWindow.responsibleAccountId,
-                                            callStackViewWindow.responsibleConvUid)
-        PluginModel.toggleCallMediaHandler(mediaHandlerId, callId, !isLoaded)
-        mediahandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(callId)
-    }
-
-    width: 350
-    height: contentItem.height
-
-    modal: true
-
-    contentItem: StackLayout {
-        id: stack
-        currentIndex: 0
-        height: childrenRect.height
-
-        Rectangle {
-            id: mediahandlerPickerPopupRect
-            width: root.width
-            height: childrenRect.height + 50
-            color: JamiTheme.backgroundColor
-            radius: 10
-
-            PushButton {
-                id: closeButton
-
-                anchors.top: mediahandlerPickerPopupRect.top
-                anchors.topMargin: 5
-                anchors.right: mediahandlerPickerPopupRect.right
-                anchors.rightMargin: 5
-
-                source: "qrc:/images/icons/round-close-24px.svg"
-                imageColor: JamiTheme.textColor
-
-                onClicked: {
-                    root.close()
-                }
-            }
-
-            ColumnLayout {
-                id: mediahandlerPickerPopupRectColumnLayout
-
-                anchors.top: mediahandlerPickerPopupRect.top
-                anchors.topMargin: 15
-                height: 230
-
-                Text {
-                    id: mediahandlerPickerTitle
-
-                    Layout.alignment: Qt.AlignCenter
-                    Layout.preferredWidth: mediahandlerPickerPopupRect.width
-                    Layout.preferredHeight: 30
-
-                    font.pointSize: JamiTheme.textFontSize
-                    font.bold: true
-
-                    horizontalAlignment: Text.AlignHCenter
-                    verticalAlignment: Text.AlignVCenter
-                    color: JamiTheme.textColor
-
-                    text: qsTr("Choose plugin")
-                }
-
-                ListView {
-                    id: mediahandlerPickerListView
-
-                    Layout.alignment: Qt.AlignCenter
-                    Layout.preferredWidth: mediahandlerPickerPopupRect.width
-                    Layout.preferredHeight: 200
-
-                    model: {
-                        var callId = UtilsAdapter.getCallId(callStackViewWindow.responsibleAccountId,
-                                                            callStackViewWindow.responsibleConvUid)
-                        return PluginAdapter.getMediaHandlerSelectableModel(callId)
-                        }
-
-                    clip: true
-
-                    delegate: MediaHandlerItemDelegate {
-                        id: mediaHandlerItemDelegate
-                        visible: PluginModel.getPluginsEnabled()
-                        width: mediahandlerPickerListView.width
-                        height: 50
-
-                        mediaHandlerName : MediaHandlerName
-                        mediaHandlerId: MediaHandlerId
-                        mediaHandlerIcon: MediaHandlerIcon
-                        isLoaded: IsLoaded
-                        pluginId: PluginId
-
-                        onBtnLoadMediaHandlerToggled: {
-                            toggleMediaHandlerSlot(mediaHandlerId, isLoaded)
-                        }
-
-                        onOpenPreferences: {
-                            mediahandlerPreferencePickerListView.pluginId = pluginId
-                            mediahandlerPreferencePickerListView.mediaHandlerName = mediaHandlerName
-                            mediahandlerPreferencePickerListView.model = PluginAdapter.getPluginPreferencesModel(pluginId, mediaHandlerName)
-                            stack.currentIndex = 1
-                        }
-                    }
-
-                    ScrollIndicator.vertical: ScrollIndicator {}
-                }
-            }
-        }
-
-        Rectangle {
-            id: mediahandlerPreferencePopupRect2
-            width: root.width
-            height: childrenRect.height + 50
-            color: JamiTheme.backgroundColor
-            radius: 10
-
-            PushButton {
-                id: backButton
-                anchors.top: mediahandlerPreferencePopupRect2.top
-                anchors.topMargin: 5
-                anchors.left: mediahandlerPreferencePopupRect2.left
-                anchors.leftMargin: 5
-
-                imageColor: JamiTheme.textColor
-                source: "qrc:/images/icons/ic_arrow_back_24px.svg"
-                toolTipText: qsTr("Go back to plugins list")
-                hoverEnabled: true
-                onClicked: {
-                    stack.currentIndex = 0
-                }
-            }
-
-            PushButton {
-                id: closeButton2
-
-                anchors.top: mediahandlerPreferencePopupRect2.top
-                anchors.topMargin: 5
-                anchors.right: mediahandlerPreferencePopupRect2.right
-                anchors.rightMargin: 5
-
-                source: "qrc:/images/icons/round-close-24px.svg"
-                imageColor: JamiTheme.textColor
-
-                onClicked: {
-                    stack.currentIndex = 0
-                    root.close()
-                }
-            }
-
-            ColumnLayout {
-
-                anchors.top: mediahandlerPreferencePopupRect2.top
-                anchors.topMargin: 15
-                height: 230
-
-                Text {
-                    Layout.alignment: Qt.AlignCenter
-                    Layout.preferredWidth: mediahandlerPreferencePopupRect2.width
-                    Layout.preferredHeight: 30
-
-                    font.pointSize: JamiTheme.textFontSize
-                    font.bold: true
-
-                    horizontalAlignment: Text.AlignHCenter
-                    verticalAlignment: Text.AlignVCenter
-
-                    color: JamiTheme.textColor
-                    text: qsTr("Preferences")
-                }
-
-                ListView {
-                    id: mediahandlerPreferencePickerListView
-                    Layout.alignment: Qt.AlignCenter
-                    Layout.preferredWidth: mediahandlerPickerPopupRect.width
-                    Layout.preferredHeight: 200
-
-                    property string pluginId: ""
-                    property string mediaHandlerName: ""
-
-                    model: PluginAdapter.getPluginPreferencesModel(pluginId, mediaHandlerName)
-
-                    clip: true
-
-                    delegate: PreferenceItemDelegate {
-                        id: mediaHandlerPreferenceDelegate
-                        width: mediahandlerPreferencePickerListView.width
-                        height: childrenRect.height
-
-                        preferenceName: PreferenceName
-                        preferenceSummary: PreferenceSummary
-                        preferenceType: PreferenceType
-                        preferenceCurrentValue: PreferenceCurrentValue
-                        pluginId: PluginId
-                        currentPath: CurrentPath
-                        preferenceKey : PreferenceKey
-                        fileFilters: FileFilters
-                        isImage: IsImage
-                        pluginListPreferenceModel: PluginListPreferenceModel{
-                            id: pluginListPreferenceModel
-                            preferenceKey : PreferenceKey
-                            pluginId: PluginId
-                        }
-
-                        onClicked:  mediahandlerPreferencePickerListView.currentIndex = index
-
-                        onBtnPreferenceClicked: {
-                            PluginModel.setPluginPreference(pluginId, preferenceKey, preferenceNewValue)
-                            mediahandlerPreferencePickerListView.model = PluginAdapter.getPluginPreferencesModel(pluginId, mediahandlerPreferencePickerListView.mediaHandlerName)
-                        }
-                    }
-
-                    ScrollIndicator.vertical: ScrollIndicator {}
-                }
-            }
-        }
-    }
-
-    onAboutToHide: stack.currentIndex = 0
-
-    onAboutToShow: {
-        // Reset the model on each show.
-        var callId = UtilsAdapter.getCallId(callStackViewWindow.responsibleAccountId,
-                                            callStackViewWindow.responsibleConvUid)
-        mediahandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(callId)
-    }
-
-    background: Rectangle {
-        color: "transparent"
-    }
-}
diff --git a/src/mainview/components/MessageWebView.qml b/src/mainview/components/MessageWebView.qml
index f68392daf..b6610f8be 100644
--- a/src/mainview/components/MessageWebView.qml
+++ b/src/mainview/components/MessageWebView.qml
@@ -26,6 +26,7 @@ import net.jami.Adapters 1.0
 import net.jami.Constants 1.0
 
 import "../../commoncomponents"
+import "../js/pluginhandlerpickercreation.js" as PluginHandlerPickerCreation
 
 Rectangle {
     id: messageWebViewRect
@@ -136,6 +137,14 @@ Rectangle {
         onNeedToHideConversationInCall: {
             messageWebViewRect.needToHideConversationInCall()
         }
+
+        onPluginSelector : {
+            // Create plugin handler picker - PLUGINS
+            PluginHandlerPickerCreation.createPluginHandlerPickerObjects(messageWebViewRect, false)
+            PluginHandlerPickerCreation.calculateCurrentGeo(
+                        messageWebViewRect.width / 2, messageWebViewRect.height / 2)
+            PluginHandlerPickerCreation.openPluginHandlerPicker()
+        }
     }
 
     QtObject {
diff --git a/src/mainview/components/MessageWebViewHeader.qml b/src/mainview/components/MessageWebViewHeader.qml
index 608279af3..2c1f2ba3a 100644
--- a/src/mainview/components/MessageWebViewHeader.qml
+++ b/src/mainview/components/MessageWebViewHeader.qml
@@ -21,6 +21,7 @@ import QtQuick.Controls 2.14
 import QtQuick.Layouts 1.14
 import net.jami.Models 1.0
 import net.jami.Constants 1.0
+import net.jami.Adapters 1.0
 
 import "../../commoncomponents"
 
@@ -34,6 +35,7 @@ Rectangle {
 
     signal backClicked
     signal needToHideConversationInCall
+    signal pluginSelector
 
     function resetBackToWelcomeViewButtonSource(reset) {
         backToWelcomeViewButtonSource = reset ? "qrc:/images/icons/ic_arrow_back_24px.svg" : "qrc:/images/icons/round-close-24px.svg"
@@ -42,6 +44,7 @@ Rectangle {
     function toggleMessagingHeaderButtonsVisible(visible) {
         startAAudioCallButton.visible = visible
         startAVideoCallButton.visible = visible
+        selectPluginButton.visible = visible
     }
 
     color: JamiTheme.secondaryBackgroundColor
@@ -155,7 +158,8 @@ Rectangle {
             PushButton {
                 id: startAVideoCallButton
 
-                anchors.right: sendContactRequestButton.visible ?
+                anchors.right:  selectPluginButton.visible ? selectPluginButton.left :
+                                   sendContactRequestButton.visible ?
                                    sendContactRequestButton.left :
                                    buttonGroup.right
                 anchors.rightMargin: 16
@@ -173,6 +177,32 @@ Rectangle {
                 }
             }
 
+            PushButton {
+                id: selectPluginButton
+
+                visible: UtilsAdapter.checkShowPluginsButton(false)
+
+                Connections {
+                    target: PluginAdapter
+                    function onPluginHandlersUpdateStatus() {
+                        selectPluginButton.visible = UtilsAdapter.checkShowPluginsButton(false)
+                    }
+                }
+
+                anchors.right: sendContactRequestButton.visible ?
+                                   sendContactRequestButton.left :
+                                   buttonGroup.right
+                anchors.rightMargin: 16
+                anchors.verticalCenter: buttonGroup.verticalCenter
+
+                source: "qrc:/images/icons/extension_24dp.svg"
+
+                normalColor: JamiTheme.secondaryBackgroundColor
+                imageColor: JamiTheme.chatviewButtonColor
+
+                onClicked: pluginSelector()
+            }
+
             PushButton {
                 id: sendContactRequestButton
 
diff --git a/src/mainview/components/MediaHandlerItemDelegate.qml b/src/mainview/components/PluginHandlerItemDelegate.qml
similarity index 89%
rename from src/mainview/components/MediaHandlerItemDelegate.qml
rename to src/mainview/components/PluginHandlerItemDelegate.qml
index 914a576b6..82d6a486a 100644
--- a/src/mainview/components/MediaHandlerItemDelegate.qml
+++ b/src/mainview/components/PluginHandlerItemDelegate.qml
@@ -30,13 +30,13 @@ import "../../commoncomponents"
 ItemDelegate {
     id: root
 
-    property string mediaHandlerName : ""
-    property string mediaHandlerId: ""
-    property string mediaHandlerIcon: ""
+    property string handlerName : ""
+    property string handlerId: ""
+    property string handlerIcon: ""
     property bool isLoaded: false
     property string pluginId: ""
 
-    signal btnLoadMediaHandlerToggled
+    signal btnLoadHandlerToggled
     signal openPreferences
 
     RowLayout{
@@ -52,7 +52,7 @@ ItemDelegate {
                 color: "transparent"
                 Image {
                     anchors.centerIn: parent
-                    source: "file:" + mediaHandlerIcon
+                    source: "file:" + handlerIcon
                     width: 30
                     height: 30
                     mipmap: true
@@ -69,7 +69,7 @@ ItemDelegate {
 
             font.pointSize: JamiTheme.settingsFontSize
             font.kerning: true
-            text: mediaHandlerName === "" ? mediaHandlerId : mediaHandlerName
+            text: handlerName === "" ? handlerId : handlerName
         }
 
         Switch {
@@ -89,7 +89,7 @@ ItemDelegate {
 
             checked: isLoaded
             onClicked: {
-                btnLoadMediaHandlerToggled()
+                btnLoadHandlerToggled()
             }
 
             background: Rectangle {
@@ -116,7 +116,7 @@ ItemDelegate {
         }
 
         PushButton {
-            id: btnPreferencesMediaHandler
+            id: btnPreferencesPluginHandler
 
             Layout.alignment: Qt.AlingVCenter | Qt.AlignRight
             Layout.rightMargin: 8
diff --git a/src/mainview/components/PluginHandlerPicker.qml b/src/mainview/components/PluginHandlerPicker.qml
new file mode 100644
index 000000000..307c5e645
--- /dev/null
+++ b/src/mainview/components/PluginHandlerPicker.qml
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Aline Gondim Santos <aline.gondimsantos@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/>.
+ */
+
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Controls.Universal 2.14
+import net.jami.Models 1.0
+import net.jami.Adapters 1.0
+import net.jami.Constants 1.0
+
+import "../../commoncomponents"
+
+Popup {
+    id: root
+
+    property bool isCall
+    property string pluginId: ""
+    property string handlerName: ""
+    signal updateProperties
+
+    width: JamiTheme.preferredDialogWidth
+    height: JamiTheme.pluginHandlersPopupViewHeight + JamiTheme.pluginHandlersPopupViewDelegateHeight
+
+    modal: true
+
+    contentItem: StackView {
+        id: stack
+        initialItem: pluginhandlerPreferenceStack
+        anchors.fill: parent
+    }
+
+    Component {
+        id: pluginhandlerPreferenceStack
+
+        Rectangle {
+            color: JamiTheme.backgroundColor
+            radius: 10
+            anchors.fill: parent
+
+            Connections {
+                target: root
+
+                function onAboutToShow(visible) {
+                    if (isCall) {
+                        // Reset the model on each show.
+                        var callId = UtilsAdapter.getCallId(callStackViewWindow.responsibleAccountId,
+                                                            callStackViewWindow.responsibleConvUid)
+                        pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(callId)
+                    } else {
+                        // Reset the model on each show.
+                        var accountId = AccountAdapter.currentAccountId
+                        var peerId = UtilsAdapter.getPeerUri(accountId, UtilsAdapter.getCurrConvId())
+                        pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(accountId, peerId)
+                    }
+                }
+            }
+
+            function toggleHandlerSlot(handlerId, isLoaded) {
+                if (isCall) {
+                    var callId = UtilsAdapter.getCallId(callStackViewWindow.responsibleAccountId,
+                                                    callStackViewWindow.responsibleConvUid)
+                    PluginModel.toggleCallMediaHandler(handlerId, callId, !isLoaded)
+                    pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(callId)
+                } else {
+                    var accountId = AccountAdapter.currentAccountId
+                    var peerId = UtilsAdapter.getPeerUri(accountId, UtilsAdapter.getCurrConvId())
+                    PluginModel.toggleChatHandler(handlerId, accountId, peerId, !isLoaded)
+                    pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(accountId, peerId)
+                }
+            }
+
+            ColumnLayout {
+                anchors.fill: parent
+                anchors.bottomMargin: 5
+
+                RowLayout {
+                    height: JamiTheme.preferredFieldHeight
+
+                    PushButton {
+                        id: closeButton
+                        Layout.leftMargin: 5
+                        Layout.topMargin: 5
+
+                        source: "qrc:/images/icons/round-close-24px.svg"
+                        imageColor: JamiTheme.textColor
+
+                        onClicked: {
+                            root.close()
+                        }
+                    }
+
+                    Text {
+                        Layout.topMargin: 10
+                        Layout.rightMargin: 5 + closeButton.width
+                        Layout.alignment: Qt.AlignCenter
+                        Layout.fillWidth: true
+
+                        font.pointSize: JamiTheme.textFontSize
+                        font.bold: true
+
+                        horizontalAlignment: Text.AlignHCenter
+                        verticalAlignment: Text.AlignVCenter
+                        color: JamiTheme.textColor
+
+                        text: qsTr("Choose plugin")
+                    }
+                }
+
+                ListView {
+                    id: pluginhandlerPickerListView
+
+                    Layout.alignment: Qt.AlignHCenter
+                    Layout.fillWidth: true
+                    Layout.fillHeight: true
+
+                    model: {
+                        if (isCall) {
+                            var callId = UtilsAdapter.getCallId(callStackViewWindow.responsibleAccountId,
+                                                                callStackViewWindow.responsibleConvUid)
+                            return PluginAdapter.getMediaHandlerSelectableModel(callId)
+                        } else {
+                            var accountId = AccountAdapter.currentAccountId
+                            var peerId = UtilsAdapter.getPeerUri(accountId, UtilsAdapter.getCurrConvId())
+                            return PluginAdapter.getChatHandlerSelectableModel(accountId, peerId)
+                        }
+                    }
+
+                    clip: true
+
+                    delegate: PluginHandlerItemDelegate {
+                        id: pluginHandlerItemDelegate
+                        visible: PluginModel.getPluginsEnabled()
+                        width: pluginhandlerPickerListView.width
+                        height: JamiTheme.pluginHandlersPopupViewDelegateHeight
+
+                        handlerName : HandlerName
+                        handlerId: HandlerId
+                        handlerIcon: HandlerIcon
+                        isLoaded: IsLoaded
+                        pluginId: PluginId
+
+                        onBtnLoadHandlerToggled: {
+                            toggleHandlerSlot(HandlerId, isLoaded)
+                        }
+
+                        onOpenPreferences: {
+                            root.pluginId = pluginId
+                            root.handlerName = handlerName
+                            stack.push(pluginhandlerPreferenceStack2, StackView.Immediate)
+                            updateProperties()
+                        }
+                    }
+
+                    ScrollIndicator.vertical: ScrollIndicator {}
+                }
+            }
+        }
+    }
+
+    Component {
+        id: pluginhandlerPreferenceStack2
+
+        Rectangle {
+            color: JamiTheme.backgroundColor
+            radius: 10
+            anchors.fill: parent
+
+            Connections {
+                target: root
+
+                function onUpdateProperties() {
+                    pluginhandlerPreferencePickerListView.pluginId = root.pluginId
+                    pluginhandlerPreferencePickerListView.handlerName = root.handlerName
+                    pluginhandlerPreferencePickerListView.model = PluginAdapter.getPluginPreferencesModel(root.pluginId, root.handlerName)
+                }
+            }
+
+            ColumnLayout {
+                anchors.fill: parent
+                anchors.bottomMargin: 5
+
+                RowLayout {
+                    height: JamiTheme.preferredFieldHeight
+
+                    PushButton {
+                        id: backButton
+                        Layout.leftMargin: 5
+                        Layout.topMargin: 5
+
+                        imageColor: JamiTheme.textColor
+                        source: "qrc:/images/icons/ic_arrow_back_24px.svg"
+                        toolTipText: qsTr("Go back to plugins list")
+                        hoverEnabled: true
+                        onClicked: {
+                            stack.pop(null, StackView.Immediate)
+                        }
+                    }
+
+                    Text {
+                        Layout.topMargin: 10
+                        Layout.alignment: Qt.AlignCenter
+                        Layout.fillWidth: true
+
+                        font.pointSize: JamiTheme.textFontSize
+                        font.bold: true
+
+                        horizontalAlignment: Text.AlignHCenter
+                        verticalAlignment: Text.AlignVCenter
+
+                        color: JamiTheme.textColor
+                        text: qsTr("Preferences")
+                    }
+
+                    PushButton {
+                        id: closeButton2
+                        Layout.rightMargin: 5
+                        Layout.topMargin: 5
+
+                        source: "qrc:/images/icons/round-close-24px.svg"
+                        imageColor: JamiTheme.textColor
+
+                        onClicked: {
+                            root.close()
+                        }
+                    }
+                }
+
+                ListView {
+                    id: pluginhandlerPreferencePickerListView
+                    Layout.alignment: Qt.AlignHCenter
+                    Layout.fillWidth: true
+                    Layout.fillHeight: true
+
+                    property string pluginId: ""
+                    property string handlerName: ""
+
+                    model: PluginAdapter.getPluginPreferencesModel(pluginId, handlerName)
+
+                    clip: true
+
+                    delegate: PreferenceItemDelegate {
+                        id: pluginHandlerPreferenceDelegate
+                        width: pluginhandlerPreferencePickerListView.width
+                        height: JamiTheme.pluginHandlersPopupViewDelegateHeight
+
+                        preferenceName: PreferenceName
+                        preferenceSummary: PreferenceSummary
+                        preferenceType: PreferenceType
+                        preferenceCurrentValue: PreferenceCurrentValue
+                        pluginId: PluginId
+                        currentPath: CurrentPath
+                        preferenceKey : PreferenceKey
+                        fileFilters: FileFilters
+                        isImage: IsImage
+                        pluginListPreferenceModel: PluginListPreferenceModel {
+                            id: handlerPickerPreferenceModel
+                            preferenceKey : PreferenceKey
+                            pluginId: PluginId
+                        }
+
+                        onClicked:  pluginhandlerPreferencePickerListView.currentIndex = index
+
+                        onBtnPreferenceClicked: {
+                            PluginModel.setPluginPreference(pluginId, preferenceKey, preferenceNewValue)
+                            PluginAdapter.pluginHandlersUpdateStatus()
+                            pluginhandlerPreferencePickerListView.model = PluginAdapter.getPluginPreferencesModel(pluginId, pluginhandlerPreferencePickerListView.handlerName)
+                        }
+                    }
+
+                    ScrollIndicator.vertical: ScrollIndicator {}
+                }
+            }
+        }
+    }
+
+    onAboutToHide: stack.pop(null, StackView.Immediate)
+
+    background: Rectangle {
+        color: "transparent"
+    }
+}
diff --git a/src/mainview/components/RecordBox.qml b/src/mainview/components/RecordBox.qml
index 0d7fb8ca0..46019e775 100644
--- a/src/mainview/components/RecordBox.qml
+++ b/src/mainview/components/RecordBox.qml
@@ -346,4 +346,3 @@ Rectangle {
         }
     }
 }
-
diff --git a/src/mainview/js/mediahandlerpickercreation.js b/src/mainview/js/mediahandlerpickercreation.js
deleted file mode 100644
index b35346c0b..000000000
--- a/src/mainview/js/mediahandlerpickercreation.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Copyright (C) 2020 by Savoir-faire Linux
- * Author: Aline Gondim Santos <aline.gondimsantos@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/>.
- */
-
-
-/*
- * Global mediahandler picker component, object variable for creation.
- */
-var mediahandlerPickerComponent
-var mediahandlerPickerObject
-
-function createMediaHandlerPickerObjects(parent) {
-    if (mediahandlerPickerObject) {
-        /*
-         * If already created, reset parameters, since object cannot be destroyed.
-         */
-        mediahandlerPickerObject.parent = parent
-        return
-    }
-    mediahandlerPickerComponent = Qt.createComponent(
-                "../components/MediaHandlerPicker.qml")
-    if (mediahandlerPickerComponent.status === Component.Ready)
-        finishCreation(parent)
-    else if (mediahandlerPickerComponent.status === Component.Error)
-        console.log("Error loading component:",
-                    mediahandlerPickerComponent.errorString())
-}
-
-function finishCreation(parent) {
-    mediahandlerPickerObject = mediahandlerPickerComponent.createObject(parent)
-    if (mediahandlerPickerObject === null) {
-        /*
-         * Error Handling.
-         */
-        console.log("Error creating object for mediahandler picker")
-    } else {
-        mediahandlerPickerObject.x = Qt.binding(function(){
-            return parent.width/2 - mediahandlerPickerObject.width / 2})
-        mediahandlerPickerObject.y = Qt.binding(function(){
-            return parent.height/2 - mediahandlerPickerObject.height / 2})
-    }
-}
-
-function openMediaHandlerPicker() {
-    if (mediahandlerPickerObject)
-        mediahandlerPickerObject.open()
-}
-
-function closeMediaHandlerPicker() {
-    if (mediahandlerPickerObject)
-        mediahandlerPickerObject.close()
-}
diff --git a/src/mainview/js/pluginhandlerpickercreation.js b/src/mainview/js/pluginhandlerpickercreation.js
new file mode 100644
index 000000000..ada943332
--- /dev/null
+++ b/src/mainview/js/pluginhandlerpickercreation.js
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Aline Gondim Santos <aline.gondimsantos@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/>.
+ */
+
+
+/*
+ * Global pluginhandler picker component, object variable for creation.
+ */
+var pluginhandlerPickerComponent
+var pluginhandlerPickerObject
+
+function createPluginHandlerPickerObjects(parent, isCall) {
+    if (pluginhandlerPickerObject) {
+        /*
+         * If already created, reset parameters, since object cannot be destroyed.
+         */
+        pluginhandlerPickerObject.parent = parent
+        return
+    }
+    pluginhandlerPickerComponent = Qt.createComponent(
+                "../components/PluginHandlerPicker.qml")
+    if (pluginhandlerPickerComponent.status === Component.Ready)
+        finishCreation(parent, isCall)
+    else if (pluginhandlerPickerComponent.status === Component.Error)
+        console.log("Error loading component:",
+                    pluginhandlerPickerComponent.errorString())
+}
+
+function finishCreation(parent, isCall) {
+    pluginhandlerPickerObject = pluginhandlerPickerComponent.createObject(parent)
+    if (pluginhandlerPickerObject === null) {
+        /*
+         * Error Handling.
+         */
+        console.log("Error creating object for pluginhandler picker")
+    } else {
+        pluginhandlerPickerObject.x = Qt.binding(function(){
+            return parent.width/2 - pluginhandlerPickerObject.width / 2})
+        pluginhandlerPickerObject.y = Qt.binding(function(){
+            return parent.height/2 - pluginhandlerPickerObject.height / 2})
+    }
+    pluginhandlerPickerObject.isCall = isCall
+}
+
+
+/*
+ * Put pluginhandler picker in the middle of container.
+ */
+function calculateCurrentGeo(containerX, containerY) {
+    if (pluginhandlerPickerObject) {
+        pluginhandlerPickerObject.x = containerX - pluginhandlerPickerObject.width / 2
+        pluginhandlerPickerObject.y = containerY - pluginhandlerPickerObject.height / 2
+    }
+}
+
+function openPluginHandlerPicker() {
+    if (pluginhandlerPickerObject)
+        pluginhandlerPickerObject.open()
+}
+
+function closePluginHandlerPicker() {
+    if (pluginhandlerPickerObject)
+        pluginhandlerPickerObject.close()
+}
diff --git a/src/mediahandleritemlistmodel.cpp b/src/mediahandleritemlistmodel.cpp
deleted file mode 100644
index 2fae003bf..000000000
--- a/src/mediahandleritemlistmodel.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/**
- * Copyright (C) 2020 by Savoir-faire Linux
- * Author: Aline Gondim Santos <aline.gondimsantos@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 "mediahandleritemlistmodel.h"
-
-MediaHandlerItemListModel::MediaHandlerItemListModel(QObject* parent, const QString& callId)
-    : QAbstractListModel(parent)
-{
-    callId_ = callId;
-}
-
-MediaHandlerItemListModel::~MediaHandlerItemListModel() {}
-
-int
-MediaHandlerItemListModel::rowCount(const QModelIndex& parent) const
-{
-    if (!parent.isValid()) {
-        /*
-         * Count.
-         */
-        return LRCInstance::pluginModel().listCallMediaHandlers().size();
-    }
-    /*
-     * A valid QModelIndex returns 0 as no entry has sub-elements.
-     */
-    return 0;
-}
-
-int
-MediaHandlerItemListModel::columnCount(const QModelIndex& parent) const
-{
-    Q_UNUSED(parent);
-    /*
-     * Only need one column.
-     */
-    return 1;
-}
-
-QVariant
-MediaHandlerItemListModel::data(const QModelIndex& index, int role) const
-{
-    auto mediahandlerList = LRCInstance::pluginModel().listCallMediaHandlers();
-    if (!index.isValid() || mediahandlerList.size() <= index.row()) {
-        return QVariant();
-    }
-
-    auto details = LRCInstance::pluginModel().getCallMediaHandlerDetails(
-        mediahandlerList.at(index.row()));
-    auto status = LRCInstance::pluginModel().getCallMediaHandlerStatus(callId_);
-    bool loaded = false;
-    for (const auto& mediaHandler : status[callId_])
-        if (mediaHandler == details.id)
-            loaded = true;
-    if (!details.pluginId.isEmpty()) {
-        details.pluginId.remove(details.pluginId.size() - 5, 5);
-    }
-
-    switch (role) {
-    case Role::MediaHandlerName:
-        return QVariant(details.name);
-    case Role::MediaHandlerId:
-        return QVariant(mediahandlerList.at(index.row()));
-    case Role::MediaHandlerIcon:
-        return QVariant(details.iconPath);
-    case Role::IsLoaded:
-        return QVariant(loaded);
-    case Role::PluginId:
-        return QVariant(details.pluginId);
-    }
-    return QVariant();
-}
-
-QHash<int, QByteArray>
-MediaHandlerItemListModel::roleNames() const
-{
-    QHash<int, QByteArray> roles;
-    roles[MediaHandlerName] = "MediaHandlerName";
-    roles[MediaHandlerId] = "MediaHandlerId";
-    roles[MediaHandlerIcon] = "MediaHandlerIcon";
-    roles[IsLoaded] = "IsLoaded";
-    roles[PluginId] = "PluginId";
-
-    return roles;
-}
-
-QModelIndex
-MediaHandlerItemListModel::index(int row, int column, const QModelIndex& parent) const
-{
-    Q_UNUSED(parent);
-    if (column != 0) {
-        return QModelIndex();
-    }
-
-    if (row >= 0 && row < rowCount()) {
-        return createIndex(row, column);
-    }
-    return QModelIndex();
-}
-
-QModelIndex
-MediaHandlerItemListModel::parent(const QModelIndex& child) const
-{
-    Q_UNUSED(child);
-    return QModelIndex();
-}
-
-Qt::ItemFlags
-MediaHandlerItemListModel::flags(const QModelIndex& index) const
-{
-    auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable;
-    if (!index.isValid()) {
-        return QAbstractItemModel::flags(index);
-    }
-    return flags;
-}
-
-void
-MediaHandlerItemListModel::reset()
-{
-    beginResetModel();
-    endResetModel();
-}
-
-QString
-MediaHandlerItemListModel::callId()
-{
-    return callId_;
-}
-
-void
-MediaHandlerItemListModel::setCallId(QString callId)
-{
-    callId_ = callId;
-}
diff --git a/src/pluginadapter.cpp b/src/pluginadapter.cpp
index 83aa1f1d5..c79fa24ac 100644
--- a/src/pluginadapter.cpp
+++ b/src/pluginadapter.cpp
@@ -27,8 +27,15 @@ PluginAdapter::PluginAdapter(QObject* parent)
 QVariant
 PluginAdapter::getMediaHandlerSelectableModel(const QString& callId)
 {
-    mediaHandlerListModel_.reset(new MediaHandlerItemListModel(this, callId));
-    return QVariant::fromValue(mediaHandlerListModel_.get());
+    pluginHandlerListModel_.reset(new PluginHandlerItemListModel(this, callId, QString("")));
+    return QVariant::fromValue(pluginHandlerListModel_.get());
+}
+
+QVariant
+PluginAdapter::getChatHandlerSelectableModel(const QString& accountId, const QString& peerId)
+{
+    pluginHandlerListModel_.reset(new PluginHandlerItemListModel(this, accountId, peerId));
+    return QVariant::fromValue(pluginHandlerListModel_.get());
 }
 
 QVariant
diff --git a/src/pluginadapter.h b/src/pluginadapter.h
index 13a1fd48a..68e993cfe 100644
--- a/src/pluginadapter.h
+++ b/src/pluginadapter.h
@@ -20,7 +20,7 @@
 
 #include "qmladapterbase.h"
 #include "pluginitemlistmodel.h"
-#include "mediahandleritemlistmodel.h"
+#include "pluginhandleritemlistmodel.h"
 #include "pluginlistpreferencemodel.h"
 #include "preferenceitemlistmodel.h"
 
@@ -38,13 +38,18 @@ public:
 protected:
     void safeInit() override {};
 
-    Q_INVOKABLE QVariant getMediaHandlerSelectableModel(const QString& callId = QString(""));
+    Q_INVOKABLE QVariant getMediaHandlerSelectableModel(const QString& callId);
+    Q_INVOKABLE QVariant getChatHandlerSelectableModel(const QString& accountId,
+                                                       const QString& peerId);
     Q_INVOKABLE QVariant getPluginSelectableModel();
     Q_INVOKABLE QVariant getPluginPreferencesModel(const QString& pluginId,
                                                    const QString& mediaHandlerName = "");
 
+signals:
+    void pluginHandlersUpdateStatus();
+
 private:
-    std::unique_ptr<MediaHandlerItemListModel> mediaHandlerListModel_;
+    std::unique_ptr<PluginHandlerItemListModel> pluginHandlerListModel_;
     std::unique_ptr<PreferenceItemListModel> preferenceItemListModel_;
     std::unique_ptr<PluginItemListModel> pluginItemListModel_;
 };
diff --git a/src/pluginhandleritemlistmodel.cpp b/src/pluginhandleritemlistmodel.cpp
new file mode 100644
index 000000000..4d7f28171
--- /dev/null
+++ b/src/pluginhandleritemlistmodel.cpp
@@ -0,0 +1,218 @@
+/**
+ * Copyright (C) 2020 by Savoir-faire Linux
+ * Author: Aline Gondim Santos <aline.gondimsantos@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 "pluginhandleritemlistmodel.h"
+
+PluginHandlerItemListModel::PluginHandlerItemListModel(QObject* parent,
+                                                       const QString& accountId,
+                                                       const QString& peerId)
+    : QAbstractListModel(parent)
+{
+    if (!peerId.isEmpty()) {
+        accountId_ = accountId;
+        peerId_ = peerId;
+        isMediaHandler_ = false;
+    } else {
+        callId_ = accountId;
+        isMediaHandler_ = true;
+    }
+}
+
+PluginHandlerItemListModel::~PluginHandlerItemListModel() {}
+
+int
+PluginHandlerItemListModel::rowCount(const QModelIndex& parent) const
+{
+    if (!parent.isValid()) {
+        /*
+         * Count.
+         */
+        if (isMediaHandler_)
+            return LRCInstance::pluginModel().getCallMediaHandlers().size();
+        else
+            return LRCInstance::pluginModel().getChatHandlers().size();
+    }
+    /*
+     * A valid QModelIndex returns 0 as no entry has sub-elements.
+     */
+    return 0;
+}
+
+int
+PluginHandlerItemListModel::columnCount(const QModelIndex& parent) const
+{
+    Q_UNUSED(parent);
+    /*
+     * Only need one column.
+     */
+    return 1;
+}
+
+QVariant
+PluginHandlerItemListModel::data(const QModelIndex& index, int role) const
+{
+    QString name {""};
+    QString id {""};
+    QString iconPath {""};
+    QString pluginId {""};
+    bool loaded = false;
+
+    if (isMediaHandler_) {
+        auto mediahandlerList = LRCInstance::pluginModel().getCallMediaHandlers();
+        if (!index.isValid() || mediahandlerList.size() <= index.row()) {
+            return QVariant();
+        }
+
+        auto details = LRCInstance::pluginModel().getCallMediaHandlerDetails(
+            mediahandlerList.at(index.row()));
+        for (const auto& mediaHandler :
+             LRCInstance::pluginModel().getCallMediaHandlerStatus(callId_))
+            if (mediaHandler == details.id)
+                loaded = true;
+        if (!details.pluginId.isEmpty()) {
+            details.pluginId.remove(details.pluginId.size() - 5, 5);
+        }
+
+        name = details.name;
+        id = mediahandlerList.at(index.row());
+        iconPath = details.iconPath;
+        pluginId = details.pluginId;
+
+    } else {
+        auto chathandlerList = LRCInstance::pluginModel().getChatHandlers();
+        if (!index.isValid() || chathandlerList.size() <= index.row()) {
+            return QVariant();
+        }
+
+        auto details = LRCInstance::pluginModel().getChatHandlerDetails(
+            chathandlerList.at(index.row()));
+        for (const auto& chatHandler :
+             LRCInstance::pluginModel().getChatHandlerStatus(accountId_, peerId_))
+            if (chatHandler == details.id)
+                loaded = true;
+        if (!details.pluginId.isEmpty()) {
+            details.pluginId.remove(details.pluginId.size() - 5, 5);
+        }
+
+        name = details.name;
+        id = chathandlerList.at(index.row());
+        iconPath = details.iconPath;
+        pluginId = details.pluginId;
+    }
+
+    switch (role) {
+    case Role::HandlerName:
+        return QVariant(name);
+    case Role::HandlerId:
+        return QVariant(id);
+    case Role::HandlerIcon:
+        return QVariant(iconPath);
+    case Role::IsLoaded:
+        return QVariant(loaded);
+    case Role::PluginId:
+        return QVariant(pluginId);
+    }
+    return QVariant();
+}
+
+QHash<int, QByteArray>
+PluginHandlerItemListModel::roleNames() const
+{
+    QHash<int, QByteArray> roles;
+    roles[HandlerName] = "HandlerName";
+    roles[HandlerId] = "HandlerId";
+    roles[HandlerIcon] = "HandlerIcon";
+    roles[IsLoaded] = "IsLoaded";
+    roles[PluginId] = "PluginId";
+
+    return roles;
+}
+
+QModelIndex
+PluginHandlerItemListModel::index(int row, int column, const QModelIndex& parent) const
+{
+    Q_UNUSED(parent);
+    if (column != 0) {
+        return QModelIndex();
+    }
+
+    if (row >= 0 && row < rowCount()) {
+        return createIndex(row, column);
+    }
+    return QModelIndex();
+}
+
+QModelIndex
+PluginHandlerItemListModel::parent(const QModelIndex& child) const
+{
+    Q_UNUSED(child);
+    return QModelIndex();
+}
+
+Qt::ItemFlags
+PluginHandlerItemListModel::flags(const QModelIndex& index) const
+{
+    auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable;
+    if (!index.isValid()) {
+        return QAbstractItemModel::flags(index);
+    }
+    return flags;
+}
+
+void
+PluginHandlerItemListModel::reset()
+{
+    beginResetModel();
+    endResetModel();
+}
+
+QString
+PluginHandlerItemListModel::callId()
+{
+    return callId_;
+}
+
+void
+PluginHandlerItemListModel::setCallId(QString callId)
+{
+    callId_ = callId;
+}
+
+QString
+PluginHandlerItemListModel::accountId()
+{
+    return accountId_;
+}
+
+void
+PluginHandlerItemListModel::setAccountId(QString accountId)
+{
+    accountId_ = accountId;
+}
+
+QString
+PluginHandlerItemListModel::peerId()
+{
+    return peerId_;
+}
+
+void
+PluginHandlerItemListModel::setPeerId(QString peerId)
+{
+    peerId_ = peerId;
+}
diff --git a/src/mediahandleritemlistmodel.h b/src/pluginhandleritemlistmodel.h
similarity index 73%
rename from src/mediahandleritemlistmodel.h
rename to src/pluginhandleritemlistmodel.h
index 7f262361f..e72bea0d9 100644
--- a/src/mediahandleritemlistmodel.h
+++ b/src/pluginhandleritemlistmodel.h
@@ -24,22 +24,20 @@
 
 #include "lrcinstance.h"
 
-class MediaHandlerItemListModel : public QAbstractListModel
+class PluginHandlerItemListModel : public QAbstractListModel
 {
     Q_OBJECT
 
 public:
-    enum Role {
-        MediaHandlerName = Qt::UserRole + 1,
-        MediaHandlerId,
-        MediaHandlerIcon,
-        IsLoaded,
-        PluginId
-    };
+    enum Role { HandlerName = Qt::UserRole + 1, HandlerId, HandlerIcon, IsLoaded, PluginId };
     Q_ENUM(Role)
 
-    explicit MediaHandlerItemListModel(QObject* parent = 0, const QString& callId = QString(""));
-    ~MediaHandlerItemListModel();
+    explicit PluginHandlerItemListModel(
+        QObject* parent = 0,
+        const QString& accountId = QString(""),
+        const QString& peerId = QString(
+            "")); // for calls, accountId is the callId and peerId is null
+    ~PluginHandlerItemListModel();
 
     /*
      * QAbstractListModel override.
@@ -62,7 +60,14 @@ public:
 
     QString callId();
     void setCallId(QString callId);
+    QString accountId();
+    void setAccountId(QString accountId);
+    QString peerId();
+    void setPeerId(QString peerId);
 
 private:
     QString callId_ = QString("");
+    QString accountId_ = QString("");
+    QString peerId_ = QString("");
+    bool isMediaHandler_;
 };
diff --git a/src/pluginitemlistmodel.cpp b/src/pluginitemlistmodel.cpp
index d1bc5e33a..d90aa45b2 100644
--- a/src/pluginitemlistmodel.cpp
+++ b/src/pluginitemlistmodel.cpp
@@ -29,7 +29,7 @@ PluginItemListModel::rowCount(const QModelIndex& parent) const
 {
     if (!parent.isValid()) {
         /// Count
-        return LRCInstance::pluginModel().listAvailablePlugins().size();
+        return LRCInstance::pluginModel().getInstalledPlugins().size();
     }
     /// A valid QModelIndex returns 0 as no entry has sub-elements.
     return 0;
@@ -46,7 +46,7 @@ PluginItemListModel::columnCount(const QModelIndex& parent) const
 QVariant
 PluginItemListModel::data(const QModelIndex& index, int role) const
 {
-    auto pluginList = LRCInstance::pluginModel().listAvailablePlugins();
+    auto pluginList = LRCInstance::pluginModel().getInstalledPlugins();
     if (!index.isValid() || pluginList.size() <= index.row()) {
         return QVariant();
     }
@@ -119,5 +119,5 @@ PluginItemListModel::reset()
 int
 PluginItemListModel::pluginsCount()
 {
-    return LRCInstance::pluginModel().listAvailablePlugins().size();
+    return LRCInstance::pluginModel().getInstalledPlugins().size();
 }
diff --git a/src/qmlregister.cpp b/src/qmlregister.cpp
index 73ad895dc..ef7b44ab4 100644
--- a/src/qmlregister.cpp
+++ b/src/qmlregister.cpp
@@ -33,7 +33,7 @@
 #include "deviceitemlistmodel.h"
 #include "distantrenderer.h"
 #include "pluginadapter.h"
-#include "mediahandleritemlistmodel.h"
+#include "pluginhandleritemlistmodel.h"
 #include "messagesadapter.h"
 #include "namedirectory.h"
 #include "updatemanager.h"
@@ -101,7 +101,7 @@ registerTypes()
     QML_REGISTERTYPE("net.jami.Models", AccountListModel, 1, 0);
     QML_REGISTERTYPE("net.jami.Models", DeviceItemListModel, 1, 0);
     QML_REGISTERTYPE("net.jami.Models", PluginItemListModel, 1, 0);
-    QML_REGISTERTYPE("net.jami.Models", MediaHandlerItemListModel, 1, 0);
+    QML_REGISTERTYPE("net.jami.Models", PluginHandlerItemListModel, 1, 0);
     QML_REGISTERTYPE("net.jami.Models", PreferenceItemListModel, 1, 0);
     QML_REGISTERTYPE("net.jami.Models", BannedListModel, 1, 0);
     QML_REGISTERTYPE("net.jami.Models", ModeratorListModel, 1, 0);
diff --git a/src/settingsview/components/PluginListPreferencesView.qml b/src/settingsview/components/PluginListPreferencesView.qml
index 8a50d3a1e..acf70bb93 100644
--- a/src/settingsview/components/PluginListPreferencesView.qml
+++ b/src/settingsview/components/PluginListPreferencesView.qml
@@ -63,6 +63,7 @@ Rectangle {
             PluginModel.resetPluginPreferencesValues(pluginId)
         }
         pluginPreferenceView.model = PluginAdapter.getPluginPreferencesModel(pluginId)
+        PluginAdapter.pluginHandlersUpdateStatus()
     }
 
     function uninstallPluginSlot() {
@@ -72,11 +73,13 @@ Rectangle {
         }]
         msgDialog.openWithParameters(qsTr("Uninstall plugin"),
                                      qsTr("Are you sure you wish to uninstall " + pluginName + " ?"))
+        PluginAdapter.pluginHandlersUpdateStatus()
     }
 
     function uninstallPlugin() {
         PluginModel.uninstallPlugin(pluginId)
         uninstalled()
+        PluginAdapter.pluginHandlersUpdateStatus()
     }
 
     function setPreference(pluginId, preferenceKey, preferenceNewValue)
@@ -87,6 +90,7 @@ Rectangle {
             PluginModel.loadPlugin(pluginId)
         } else
             PluginModel.setPluginPreference(pluginId, preferenceKey, preferenceNewValue)
+        PluginAdapter.pluginHandlersUpdateStatus()
     }
 
     SimpleMessageDialog {
diff --git a/src/settingsview/components/PluginListSettingsView.qml b/src/settingsview/components/PluginListSettingsView.qml
index 89ded92ea..96dbd33c7 100644
--- a/src/settingsview/components/PluginListSettingsView.qml
+++ b/src/settingsview/components/PluginListSettingsView.qml
@@ -56,6 +56,7 @@ Rectangle {
             loaded = PluginModel.loadPlugin(pluginId)
         if (pluginListPreferencesView.pluginId === pluginId)
             pluginListPreferencesView.isLoaded = loaded
+        PluginAdapter.pluginHandlersUpdateStatus()
     }
 
     function openPreferencesPluginSlot(pluginName, pluginIcon, pluginId, isLoaded) {
diff --git a/src/settingsview/components/PluginSettingsPage.qml b/src/settingsview/components/PluginSettingsPage.qml
index 4581f4032..286637d14 100644
--- a/src/settingsview/components/PluginSettingsPage.qml
+++ b/src/settingsview/components/PluginSettingsPage.qml
@@ -40,6 +40,7 @@ Rectangle {
 
     function slotSetPluginEnabled(state) {
         PluginModel.setPluginsEnabled(state)
+        PluginAdapter.pluginHandlersUpdateStatus()
     }
 
     color: JamiTheme.secondaryBackgroundColor
diff --git a/src/utilsadapter.cpp b/src/utilsadapter.cpp
index d17ebd012..49eeeff6b 100644
--- a/src/utilsadapter.cpp
+++ b/src/utilsadapter.cpp
@@ -110,6 +110,14 @@ UtilsAdapter::getBestName(const QString& accountId, const QString& uid)
     return QString();
 }
 
+const QString
+UtilsAdapter::getPeerUri(const QString& accountId, const QString& uid)
+{
+    auto* convModel = LRCInstance::getAccountInfo(accountId).conversationModel.get();
+    const auto& convInfo = convModel->getConversationForUid(uid).value();
+    return convInfo.get().participants.front();
+}
+
 QString
 UtilsAdapter::getBestId(const QString& accountId)
 {
@@ -316,10 +324,14 @@ UtilsAdapter::getAbsPath(QString path)
 }
 
 bool
-UtilsAdapter::checkShowPluginsButton()
-{
-    return LRCInstance::pluginModel().getPluginsEnabled()
-           && (LRCInstance::pluginModel().listLoadedPlugins().size() > 0);
+UtilsAdapter::checkShowPluginsButton(bool isCall)
+{
+    if (isCall)
+        return LRCInstance::pluginModel().getPluginsEnabled()
+               && (LRCInstance::pluginModel().getCallMediaHandlers().size() > 0);
+    else
+        return LRCInstance::pluginModel().getPluginsEnabled()
+               && (LRCInstance::pluginModel().getChatHandlers().size() > 0);
 }
 
 QString
diff --git a/src/utilsadapter.h b/src/utilsadapter.h
index b8615af08..88b773393 100644
--- a/src/utilsadapter.h
+++ b/src/utilsadapter.h
@@ -47,6 +47,7 @@ public:
     Q_INVOKABLE int getTotalUnreadMessages();
     Q_INVOKABLE int getTotalPendingRequest();
     Q_INVOKABLE const QString getBestName(const QString& accountId, const QString& uid);
+    Q_INVOKABLE const QString getPeerUri(const QString& accountId, const QString& uid);
     Q_INVOKABLE QString getBestId(const QString& accountId);
     Q_INVOKABLE const QString getBestId(const QString& accountId, const QString& uid);
     Q_INVOKABLE const QString getCurrConvId();
@@ -68,7 +69,7 @@ public:
     Q_INVOKABLE QString toFileInfoName(QString inputFileName);
     Q_INVOKABLE QString toFileAbsolutepath(QString inputFileName);
     Q_INVOKABLE QString getAbsPath(QString path);
-    Q_INVOKABLE bool checkShowPluginsButton();
+    Q_INVOKABLE bool checkShowPluginsButton(bool isCall);
     Q_INVOKABLE QString fileName(const QString& path);
     Q_INVOKABLE QString getExt(const QString& path);
     Q_INVOKABLE bool isImage(const QString& fileExt);
-- 
GitLab