diff --git a/images/icons/add_people_black_24dp.svg b/images/icons/add_people_black_24dp.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2bc314f7d92022393cd40ac5782472ad40063b05
--- /dev/null
+++ b/images/icons/add_people_black_24dp.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<path d="M16.1,11.3c1.1-0.7,1.8-2,1.8-3.3c0-2.2-1.8-4-4-4c-2.2,0-4,1.8-4,4c0,1.3,0.7,2.6,1.8,3.3c-0.6,0.3-1.2,0.6-1.8,1.1
+	c-0.3-0.3-0.6-0.5-1-0.7c0.6-0.6,1-1.4,1-2.3c0-1.8-1.4-3.2-3.2-3.2c-1.8,0-3.2,1.4-3.2,3.2c0,0.9,0.4,1.7,1,2.3
+	c-1.5,0.8-2.5,2.4-2.5,4.2c0,0.6,0.5,1.1,1.1,1.1h4.7c0,0.6,0.5,1.1,1.1,1.1h6.5l-0.3-0.2c-0.3-0.2-0.5-0.6-0.7-1l0-0.1H9.1
+	c0-0.3,0.1-0.7,0.1-1c0.1-0.6,0.4-1.1,0.7-1.6c0.2-0.3,0.4-0.6,0.7-0.8c0.9-0.8,2.1-1.3,3.3-1.3c1.1,0,2.1,0.4,3,1l0.1,0.1l0.1-0.1
+	c0.1-0.2,0.2-0.4,0.4-0.6c0.1-0.1,0.1-0.1,0.2-0.2l0.1-0.1l-0.1-0.1C17.2,11.8,16.6,11.5,16.1,11.3z M16.6,7.9
+	c0,1.5-1.2,2.7-2.7,2.7c-1.5,0-2.7-1.2-2.7-2.7c0-1.5,1.2-2.7,2.7-2.7C15.4,5.2,16.6,6.4,16.6,7.9z M6.6,11.3c-1.1,0-2-0.9-2-2
+	c0-1.1,0.9-2,2-2c1.1,0,2,0.9,2,2C8.6,10.4,7.7,11.3,6.6,11.3z M9,13.3c-0.5,0.7-0.9,1.5-1.1,2.4H3.2c0.1-1.8,1.6-3.3,3.4-3.3
+	C7.5,12.4,8.4,12.7,9,13.3z"/>
+<path d="M21.2,15.6l-1.7,0l0-1.7c0-0.4-0.3-0.7-0.7-0.7c-0.2,0-0.4,0.1-0.5,0.2c-0.1,0.1-0.2,0.3-0.2,0.5l0,1.7l-1.7,0
+	c-0.2,0-0.4,0.1-0.5,0.2c-0.1,0.1-0.2,0.3-0.2,0.5c0,0.4,0.3,0.7,0.7,0.7l1.7,0l0,1.7c0,0.4,0.3,0.7,0.7,0.7h0
+	c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.5l0-1.7l1.7,0c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.5
+	C21.9,16,21.6,15.7,21.2,15.6z"/>
+</svg>
diff --git a/images/icons/chat_black_24dp.svg b/images/icons/chat_black_24dp.svg
new file mode 100644
index 0000000000000000000000000000000000000000..867ba122d8f89c57e0a98380218fc21e509b0f9a
--- /dev/null
+++ b/images/icons/chat_black_24dp.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<g id="Icones_Outline">
+	<g id="Chat_Black_24dp">
+		<g id="Shape" transform="translate(3.000000, 4.000000)">
+			<g>
+				<path d="M4.6,17c-0.2,0-0.4,0-0.6-0.1c-0.6-0.3-0.9-0.8-0.9-1.5V13H2.3C0.5,13-1,11.5-1,9.6V2.3C-1,0.5,0.5-1,2.3-1h13.3
+					C17.5-1,19,0.5,19,2.3v7.3c0,1.8-1.5,3.3-3.3,3.3H9.5l-3.8,3.6C5.4,16.8,5,17,4.6,17z M2.3,0.4c-1.1,0-1.9,0.9-1.9,2v7.3
+					c0,1,0.8,1.9,1.9,1.9h2.2v3.9c0,0,0,0.1,0.1,0.1s0.1,0,0.2,0l4.2-4h6.7c1,0,1.9-0.8,1.9-1.9V2.3c0-1-0.8-1.9-1.9-1.9H2.3
+					L2.3,0.4z"/>
+			</g>
+		</g>
+	</g>
+</g>
+</svg>
diff --git a/images/icons/record_black_24dp.svg b/images/icons/record_black_24dp.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8f5bb1ac5a714854b3d2ef8949e287816d8dded4
--- /dev/null
+++ b/images/icons/record_black_24dp.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-4.5,10-10S17.5,2,12,2z M12,20.3c-4.6,0-8.3-3.7-8.3-8.3S7.4,3.7,12,3.7
+	s8.3,3.7,8.3,8.3S16.6,20.3,12,20.3z"/>
+</svg>
diff --git a/images/icons/share_screen_black_24dp.svg b/images/icons/share_screen_black_24dp.svg
new file mode 100644
index 0000000000000000000000000000000000000000..de0e6a11b1749dab59038fac2f54bb7ac9231036
--- /dev/null
+++ b/images/icons/share_screen_black_24dp.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<path id="Path" d="M12.6,8.9V7.8l2.6,2.2l-2.6,2.2V11c-3.4,0.1-4.2,2.6-4.2,2.6C8.5,9.6,11.7,9,12.6,8.9z"/>
+<g id="Icones_Outline">
+	<g id="Laptop_Black_24dp">
+		<g transform="translate(2.000000, 5.000000)">
+			<g id="Shape">
+				<path d="M17,12H2.8c-0.5,0-0.9-0.2-1.2-0.5C1.2,11.1,1,10.7,1,10.2V1.3c0-0.5,0.2-0.9,0.5-1.2s0.8-0.5,1.2-0.5H17
+					c0.5,0,0.9,0.2,1.2,0.5c0.3,0.3,0.5,0.8,0.5,1.2v8.9c0,0.5-0.2,0.9-0.5,1.2S17.5,12,17,12z M2.8,0.9C2.7,0.9,2.6,1,2.5,1
+					c0,0.1-0.1,0.2-0.1,0.3v8.9c0,0.1,0,0.2,0.1,0.3c0,0,0.1,0.1,0.3,0.1H17c0.1,0,0.2-0.1,0.3-0.1c0.1-0.1,0.1-0.2,0.1-0.3V1.3
+					c0-0.1,0-0.2-0.1-0.3c0,0-0.1-0.1-0.3-0.1C17,0.9,2.8,0.9,2.8,0.9z"/>
+			</g>
+			<g id="Line-2">
+				<path d="M19.5,14.4h-19c-0.4,0-0.7-0.3-0.7-0.7S0.1,13,0.5,13h19c0.4,0,0.7,0.3,0.7,0.7S19.9,14.4,19.5,14.4z"/>
+			</g>
+		</g>
+	</g>
+</g>
+</svg>
diff --git a/images/icons/spk_black_24dp.svg b/images/icons/spk_black_24dp.svg
new file mode 100644
index 0000000000000000000000000000000000000000..106a2711a23f287abb4ed8fd1969eeb06c665ba9
--- /dev/null
+++ b/images/icons/spk_black_24dp.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<g>
+	<path d="M14.6,4c-0.4-0.7-1.3-0.8-2-0.4L6.8,7.7H4.1C3,7.7,2,8.7,2,9.9v4.3c0,1.2,1,2.1,2.1,2.1h2.6l5.8,4.1
+		c0.2,0.2,0.5,0.2,0.8,0.2c0.8,0,1.4-0.6,1.5-1.4V4.8C14.9,4.5,14.8,4.2,14.6,4z M13.4,19.2L13.4,19.2l-5.7-4l-0.4-0.3h-1H6.1h-2
+		c-0.4,0-0.7-0.3-0.7-0.7V9.9c0-0.4,0.3-0.7,0.7-0.7H6h0.2h1l0.4-0.3l5.7-4L13.4,19.2L13.4,19.2z"/>
+	<path d="M19.1,12c0,1.5-0.6,2.9-1.6,4c-0.3,0.3-0.7,0.3-1,0c-0.3-0.3-0.3-0.7,0-1c1.6-1.7,1.6-4.3,0-6c-0.3-0.3-0.3-0.7,0-1
+		c0.3-0.3,0.7-0.3,1,0C18.6,9.1,19.1,10.5,19.1,12z"/>
+	<path d="M18.9,5.1c-0.3-0.3-0.7-0.2-1,0c-0.3,0.3-0.2,0.7,0,1c3.3,2.9,3.5,8,0.6,11.2c-0.2,0.2-0.4,0.4-0.6,0.6
+		c-0.3,0.3-0.3,0.7,0,1c0.3,0.3,0.7,0.3,1,0c3.8-3.5,4.1-9.4,0.7-13.2C19.4,5.5,19.1,5.3,18.9,5.1L18.9,5.1z"/>
+</g>
+</svg>
diff --git a/images/icons/spk_none_black_24dp.svg b/images/icons/spk_none_black_24dp.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0c2c89b6bdeeee845715bb26dcbc0990796ac927
--- /dev/null
+++ b/images/icons/spk_none_black_24dp.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
+<g>
+	<path d="M14.6,4c-0.4-0.7-1.3-0.8-2-0.4L6.8,7.7H4.1C3,7.7,2,8.7,2,9.9v4.3c0,1.2,1,2.1,2.1,2.1h2.6l5.8,4.1
+		c0.2,0.2,0.5,0.2,0.8,0.2c0.8,0,1.4-0.6,1.5-1.4V4.8C14.9,4.5,14.8,4.2,14.6,4z M13.4,19.2L13.4,19.2l-5.7-4l-0.4-0.3h-1H6.1h-2
+		c-0.4,0-0.7-0.3-0.7-0.7V9.9c0-0.4,0.3-0.7,0.7-0.7H6h0.2h1l0.4-0.3l5.7-4L13.4,19.2L13.4,19.2z"/>
+	<path d="M19.1,12c0,1.5-0.6,2.9-1.6,4c-0.3,0.3-0.7,0.3-1,0c-0.3-0.3-0.3-0.7,0-1c1.6-1.7,1.6-4.3,0-6c-0.3-0.3-0.3-0.7,0-1
+		c0.3-0.3,0.7-0.3,1,0C18.6,9.1,19.1,10.5,19.1,12z"/>
+	<path d="M18.9,5.1c-0.3-0.3-0.7-0.2-1,0c-0.3,0.3-0.2,0.7,0,1c3.3,2.9,3.5,8,0.6,11.2c-0.2,0.2-0.4,0.4-0.6,0.6
+		c-0.3,0.3-0.3,0.7,0,1c0.3,0.3,0.7,0.3,1,0c3.8-3.5,4.1-9.4,0.7-13.2C19.4,5.5,19.1,5.3,18.9,5.1L18.9,5.1z"/>
+</g>
+<g>
+	<path d="M3.3,20.7c-0.2,0-0.3-0.1-0.5-0.2c-0.3-0.3-0.3-0.7-0.1-1L16.3,3.7c0.3-0.3,0.7-0.3,1-0.1c0.3,0.3,0.3,0.7,0.1,1L3.8,20.5
+		C3.7,20.6,3.5,20.7,3.3,20.7z"/>
+</g>
+</svg>
diff --git a/qml.qrc b/qml.qrc
index 5b974ba7e5de68a4f0d5f8855f3f95fc2fc2e0b4..80b9a37d1c914f703e4ac31fa3f436e05f4201e5 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -106,7 +106,6 @@
         <file>src/mainview/components/CallStackView.qml</file>
         <file>src/mainview/components/InitialCallPage.qml</file>
         <file>src/mainview/components/CallOverlay.qml</file>
-        <file>src/mainview/components/CallOverlayButtonGroup.qml</file>
         <file>src/mainview/components/ContactSearchBar.qml</file>
         <file>src/mainview/components/OngoingCallPage.qml</file>
         <file>src/mainview/components/ParticipantOverlay.qml</file>
@@ -137,5 +136,10 @@
         <file>src/mainview/components/SmartListItemDelegate.qml</file>
         <file>src/mainview/components/BadgeNotifier.qml</file>
         <file>src/mainview/components/ParticipantsLayer.qml</file>
+        <file>src/mainview/components/MainOverlay.qml</file>
+        <file>src/mainview/components/CallButtonDelegate.qml</file>
+        <file>src/mainview/components/CallActionBar.qml</file>
+        <file>src/commoncomponents/HalfPill.qml</file>
+        <file>src/commoncomponents/MaterialToolTip.qml</file>
     </qresource>
 </RCC>
diff --git a/resources.qrc b/resources.qrc
index 0766587550e62cd8cacc39f26324345609af293c..bbfe9d5062827485754194bac0e29be2e12daea0 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -134,5 +134,11 @@
         <file>images/icons/settings-24px.svg</file>
         <file>images/icons/quote.svg</file>
         <file>images/icons/plugins-24px.svg</file>
+        <file>images/icons/record_black_24dp.svg</file>
+        <file>images/icons/share_screen_black_24dp.svg</file>
+        <file>images/icons/chat_black_24dp.svg</file>
+        <file>images/icons/add_people_black_24dp.svg</file>
+        <file>images/icons/spk_black_24dp.svg</file>
+        <file>images/icons/spk_none_black_24dp.svg</file>
     </qresource>
 </RCC>
diff --git a/src/audiodevicemodel.cpp b/src/audiodevicemodel.cpp
index 9dd71c82a22e4702c5debc4bde42fefbf4abb6b4..9bb6e7d80f19e8453d8f7dcfc808504aca8c1359 100644
--- a/src/audiodevicemodel.cpp
+++ b/src/audiodevicemodel.cpp
@@ -62,6 +62,8 @@ AudioDeviceModel::data(const QModelIndex& index, int role) const
     }
     case Role::RawDeviceName:
         return QVariant(devices_.at(index.row()));
+    case Role::isCurrent:
+        return QVariant(index.row() == getCurrentIndex());
     default:
         break;
     }
@@ -115,7 +117,7 @@ AudioDeviceModel::reset()
 }
 
 int
-AudioDeviceModel::getCurrentIndex()
+AudioDeviceModel::getCurrentIndex() const
 {
     QString currentId = lrcInstance_->avModel().getInputDevice();
     auto resultList = match(index(0, 0), Qt::DisplayRole, QVariant(currentId));
diff --git a/src/audiodevicemodel.h b/src/audiodevicemodel.h
index e8cf37e9bb62547dd9ba71879a71544a4c368a4b..11a966cbb2b1002d9657d6bf584f504a4cc398ef 100644
--- a/src/audiodevicemodel.h
+++ b/src/audiodevicemodel.h
@@ -28,7 +28,7 @@ public:
     Q_ENUM(Type)
     Q_PROPERTY(Type type MEMBER type_ NOTIFY typeChanged)
 
-    enum Role { DeviceName = Qt::UserRole + 1, RawDeviceName };
+    enum Role { DeviceName = Qt::UserRole + 1, RawDeviceName, isCurrent };
     Q_ENUM(Role)
 
 Q_SIGNALS:
@@ -56,10 +56,10 @@ public:
     Qt::ItemFlags flags(const QModelIndex& index) const override;
 
     Q_INVOKABLE void reset();
-    Q_INVOKABLE int getCurrentIndex();
+    Q_INVOKABLE int getCurrentIndex() const;
 
 private:
     QVector<QString> devices_;
 
     Type type_ {Type::Invalid};
-};
\ No newline at end of file
+};
diff --git a/src/avadapter.cpp b/src/avadapter.cpp
index b9101139fcfb297d1e88ba1eff017a9b8594a506..d8d555ed752f2fa0c7bd52e83ebdda59ad38fcb6 100644
--- a/src/avadapter.cpp
+++ b/src/avadapter.cpp
@@ -69,13 +69,19 @@ AvAdapter::populateVideoDeviceContextMenuItem()
 }
 
 void
-AvAdapter::onVideoContextMenuDeviceItemClicked(const QString& deviceName)
+AvAdapter::selectVideoInputDeviceByName(const QString& deviceName)
 {
     auto deviceId = lrcInstance_->avModel().getDeviceIdFromName(deviceName);
     if (deviceId.isEmpty()) {
         qWarning() << "Couldn't find device: " << deviceName;
         return;
     }
+    selectVideoInputDeviceById(deviceId);
+}
+
+void
+AvAdapter::selectVideoInputDeviceById(const QString& deviceId)
+{
     lrcInstance_->avModel().setCurrentVideoCaptureDevice(deviceId);
     lrcInstance_->avModel().switchInputTo(deviceId, getCurrentCallId());
 }
diff --git a/src/avadapter.h b/src/avadapter.h
index ff6a56046127113cfc13e1fc874c32c5b77772d3..c0347999f9fbb8eecaf90caa677151d1bca84c94 100644
--- a/src/avadapter.h
+++ b/src/avadapter.h
@@ -45,8 +45,11 @@ protected:
     // Return needed info for populating video device context menu item.
     Q_INVOKABLE QVariantMap populateVideoDeviceContextMenuItem();
 
-    // Preview video input switching.
-    Q_INVOKABLE void onVideoContextMenuDeviceItemClicked(const QString& deviceName);
+    // switch preview video input by device name
+    Q_INVOKABLE void selectVideoInputDeviceByName(const QString& deviceName);
+
+    // switch preview video input by device id
+    Q_INVOKABLE void selectVideoInputDeviceById(const QString& deviceId);
 
     // Share the screen specificed by screen number.
     Q_INVOKABLE void shareEntireScreen(int screenNumber);
diff --git a/src/calladapter.cpp b/src/calladapter.cpp
index 22fc61bb6012c3551622aae93211b4fb73c84176..57f70c1bb898469410ba337fac1457e92e056bac 100644
--- a/src/calladapter.cpp
+++ b/src/calladapter.cpp
@@ -639,7 +639,6 @@ CallAdapter::updateCallOverlay(const lrc::api::conversation::Info& convInfo)
                          isVideoMuted,
                          isRecording,
                          accInfo.profileInfo.type == lrc::api::profile::Type::SIP,
-                         !convInfo.confId.isEmpty(),
                          bestName);
 }
 
diff --git a/src/calladapter.h b/src/calladapter.h
index bdd7015d798f679d730d93f4946d0f350dcf841c..06d9b84aa93e4d81ca36be5334b5c9caab734358 100644
--- a/src/calladapter.h
+++ b/src/calladapter.h
@@ -95,7 +95,6 @@ Q_SIGNALS:
                        bool isVideoMuted,
                        bool isRecording,
                        bool isSIP,
-                       bool isConferenceCall,
                        const QString& bestName);
     void remoteRecordingChanged(const QStringList& peers, bool state);
     void eraseRemoteRecording();
diff --git a/src/calloverlaymodel.cpp b/src/calloverlaymodel.cpp
index fb130de346dd4016665c1a2a1fd8b029e237f95b..beb49d0198d186397c92b7c50e5c5ef8ebe46c6f 100644
--- a/src/calloverlaymodel.cpp
+++ b/src/calloverlaymodel.cpp
@@ -44,15 +44,10 @@ CallControlListModel::data(const QModelIndex& index, int role) const
     auto item = data_.at(index.row());
 
     switch (role) {
-    case Role::DummyRole:
-        break;
-#define X(t, role) \
-    case Role::role: \
-        return QVariant::fromValue(item.role);
-        CC_ROLES
-#undef X
-    default:
-        break;
+    case Role::ItemAction:
+        return QVariant::fromValue(item.itemAction);
+    case Role::BadgeCount:
+        return QVariant::fromValue(item.badgeCount);
     }
     return QVariant();
 }
@@ -62,9 +57,8 @@ CallControlListModel::roleNames() const
 {
     using namespace CallControl;
     QHash<int, QByteArray> roles;
-#define X(t, role) roles[role] = #role;
-    CC_ROLES
-#undef X
+    roles[ItemAction] = "ItemAction";
+    roles[BadgeCount] = "BadgeCount";
     return roles;
 }
 
@@ -73,7 +67,7 @@ CallControlListModel::setBadgeCount(int row, int count)
 {
     if (row >= rowCount())
         return;
-    data_[row].BadgeCount = count;
+    data_[row].badgeCount = count;
     auto idx = index(row, 0);
     Q_EMIT dataChanged(idx, idx);
 }
@@ -86,6 +80,12 @@ CallControlListModel::addItem(const CallControl::Item& item)
     endResetModel();
 }
 
+void
+CallControlListModel::clearData()
+{
+    data_.clear();
+}
+
 IndexRangeFilterProxyModel::IndexRangeFilterProxyModel(QAbstractListModel* parent)
     : QSortFilterProxyModel(parent)
 {
@@ -129,25 +129,15 @@ CallOverlayModel::CallOverlayModel(LRCInstance* instance, QObject* parent)
 }
 
 void
-CallOverlayModel::addPrimaryControl(const QVariantMap& props)
+CallOverlayModel::addPrimaryControl(const QVariant& action)
 {
-    CallControl::Item item {
-#define X(t, role) props[#role].value<t>(),
-        CC_ROLES
-#undef X
-    };
-    primaryModel_->addItem(item);
+    primaryModel_->addItem(CallControl::Item {action.value<QObject*>()});
 }
 
 void
-CallOverlayModel::addSecondaryControl(const QVariantMap& props)
-{
-    CallControl::Item item {
-#define X(t, role) props[#role].value<t>(),
-        CC_ROLES
-#undef X
-    };
-    secondaryModel_->addItem(item);
+CallOverlayModel::addSecondaryControl(const QVariant& action)
+{
+    secondaryModel_->addItem(CallControl::Item {action.value<QObject*>()});
     setControlRanges();
 }
 
@@ -187,6 +177,13 @@ CallOverlayModel::overflowHiddenModel()
     return QVariant::fromValue(overflowHiddenModel_);
 }
 
+void
+CallOverlayModel::clearControls()
+{
+    primaryModel_->clearData();
+    secondaryModel_->clearData();
+}
+
 void
 CallOverlayModel::registerFilter(QQuickWindow* object, QQuickItem* item)
 {
diff --git a/src/calloverlaymodel.h b/src/calloverlaymodel.h
index 856a71a3dd91d78aaf0d97b668aff37ca199f36c..1776184a9587a0a7eb6c1e32639d0cf05f7b0646 100644
--- a/src/calloverlaymodel.h
+++ b/src/calloverlaymodel.h
@@ -27,28 +27,15 @@
 #include <QSortFilterProxyModel>
 #include <QQuickItem>
 
-#define CC_ROLES \
-    X(QObject*, ItemAction) \
-    X(int, BadgeCount) \
-    X(bool, HasBackground) \
-    X(QObject*, MenuAction) \
-    X(QString, Name)
-
 namespace CallControl {
 Q_NAMESPACE
-enum Role {
-    DummyRole = Qt::UserRole + 1,
-#define X(t, role) role,
-    CC_ROLES
-#undef X
-};
+enum Role { ItemAction = Qt::UserRole + 1, BadgeCount };
 Q_ENUM_NS(Role)
 
 struct Item
 {
-#define X(t, role) t role;
-    CC_ROLES
-#undef X
+    QObject* itemAction;
+    int badgeCount {0};
 };
 } // namespace CallControl
 
@@ -64,6 +51,7 @@ public:
 
     void setBadgeCount(int row, int count);
     void addItem(const CallControl::Item& item);
+    void clearData();
 
 private:
     QList<CallControl::Item> data_;
@@ -93,9 +81,10 @@ class CallOverlayModel : public QObject
 public:
     CallOverlayModel(LRCInstance* instance, QObject* parent = nullptr);
 
-    Q_INVOKABLE void addPrimaryControl(const QVariantMap& props);
-    Q_INVOKABLE void addSecondaryControl(const QVariantMap& props);
+    Q_INVOKABLE void addPrimaryControl(const QVariant& action);
+    Q_INVOKABLE void addSecondaryControl(const QVariant& action);
     Q_INVOKABLE void setBadgeCount(int row, int count);
+    Q_INVOKABLE void clearControls();
 
     Q_INVOKABLE QVariant primaryModel();
     Q_INVOKABLE QVariant secondaryModel();
diff --git a/src/commoncomponents/HalfPill.qml b/src/commoncomponents/HalfPill.qml
new file mode 100644
index 0000000000000000000000000000000000000000..6d9564eeb79c10d41533178c7777ffc1c4aac7c2
--- /dev/null
+++ b/src/commoncomponents/HalfPill.qml
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.14
+
+Item {
+    id: root
+
+    enum Type {
+        None,
+        Top,
+        Left,
+        Bottom,
+        Right
+    }
+
+    property int type: HalfPill.None
+    property int radius: 0
+    property alias color: rect.color
+
+    clip: true
+
+    Rectangle {
+        id: rect
+
+        property bool horizontal: type === HalfPill.Left ||
+                                  type == HalfPill.Right
+        property bool direction: type === HalfPill.Right ||
+                                 type == HalfPill.Bottom
+
+        radius: root.radius * (type !== HalfPill.None)
+        width: root.size + radius
+        height: root.size + radius
+        anchors.fill: root
+        anchors.leftMargin: horizontal * direction * -radius
+        anchors.rightMargin: horizontal * !direction * -radius
+        anchors.topMargin: !horizontal * direction * -radius
+        anchors.bottomMargin: !horizontal * !direction * -radius
+    }
+}
diff --git a/src/commoncomponents/MaterialToolTip.qml b/src/commoncomponents/MaterialToolTip.qml
new file mode 100644
index 0000000000000000000000000000000000000000..f7e1a41059afe74f3670312169406ba59cc4f29e
--- /dev/null
+++ b/src/commoncomponents/MaterialToolTip.qml
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.0
+import QtQuick.Controls 2.14
+
+import net.jami.Constants 1.0
+
+ToolTip {
+    id: root
+
+    onVisibleChanged: {
+        if (visible)
+            animation.start()
+    }
+
+    contentItem: Text {
+        id: label
+        text: root.text
+        font: root.font
+        color: "white"
+    }
+
+    background: Rectangle {
+        color: "#c4272727"
+        radius: 5
+    }
+
+    ParallelAnimation {
+        id: animation
+        NumberAnimation {
+             target: root; properties: "opacity"
+             from: 0; to: 1.0
+             duration: JamiTheme.shortFadeDuration
+        }
+        NumberAnimation {
+             target: root; properties: "scale"
+             from: 0.5; to: 1.0
+             duration: JamiTheme.shortFadeDuration * 0.5
+        }
+    }
+}
diff --git a/src/commoncomponents/ResponsiveImage.qml b/src/commoncomponents/ResponsiveImage.qml
index a727c627d49c1128931a63cdded4b76dfc1c667c..a703d23a99a375b5b287cfb498e76dd97a9f283e 100644
--- a/src/commoncomponents/ResponsiveImage.qml
+++ b/src/commoncomponents/ResponsiveImage.qml
@@ -25,8 +25,8 @@ import net.jami.Models 1.0
 Image {
     id: root
 
-    property real containerWidth
-    property real containerHeight
+    property real containerWidth: 30
+    property real containerHeight: 30
 
     property int padding: 0
     property point offset: Qt.point(0, 0)
diff --git a/src/constant/JamiStrings.qml b/src/constant/JamiStrings.qml
index aa61f241b2207bdcb180816ca5f8e57f9a8bc5bb..208c73b7c7bc5657f4e07e27b7150d9bdfa1e4eb 100644
--- a/src/constant/JamiStrings.qml
+++ b/src/constant/JamiStrings.qml
@@ -186,7 +186,7 @@ Item {
     property string peerStoppedRecording: qsTr("Peer stopped recording")
     property string isCallingYou: qsTr("is calling you")
 
-    // CallOverlayButtonGroup
+    // CallOverlay
     property string mute: qsTr("Mute")
     property string unmute: qsTr("Unmute")
     property string hangup: qsTr("End call")
diff --git a/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml
index f70caac3eea636a2c25b84493a514f7695c30916..3cb860b186443d3102e0150f567e7139ac4efeee 100644
--- a/src/constant/JamiTheme.qml
+++ b/src/constant/JamiTheme.qml
@@ -164,8 +164,9 @@ Item {
     property color bgDarkMode_: rgba256(32, 32, 32, 100)
 
     property int shortFadeDuration: 150
-    property int overlayFadeDelay: 2000
-    property int overlayFadeDuration: 500
+    property int recordBlinkDuration: 500
+    property int overlayFadeDelay: 4000
+    property int overlayFadeDuration: 250
     property int smartListTransitionDuration: 120
 
     // Sizes
diff --git a/src/mainview/components/CallActionBar.qml b/src/mainview/components/CallActionBar.qml
new file mode 100644
index 0000000000000000000000000000000000000000..67cdb0613cf9cdfb5099088baad0a5ec64e8baa4
--- /dev/null
+++ b/src/mainview/components/CallActionBar.qml
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+
+import net.jami.Models 1.0
+import net.jami.Adapters 1.0
+import net.jami.Constants 1.0
+
+import "../../commoncomponents"
+
+Control {
+    id: root
+
+    property alias overflowOpen: overflowButton.popup.visible
+    property bool subMenuOpen: false
+
+    property real itemSpacing: 2
+    property bool localIsRecording: false
+
+    signal chatClicked
+    signal addToConferenceClicked
+    signal transferClicked // TODO: bind this
+    signal shareScreenClicked
+    signal shareScreenAreaClicked // TODO: bind this
+    signal pluginsClicked
+
+    Component {
+        id: buttonDelegate
+
+        CallButtonDelegate {
+            width: root.height
+            height: width
+            onSubMenuVisibleChanged: subMenuOpen = subMenuVisible
+        }
+    }
+
+    Connections {
+        target: AvAdapter
+
+        // TODO: audio device list updates
+        function onVideoDeviceListChanged(listIsEmpty) {
+            videoInputDeviceListModel.reset();
+        }
+    }
+
+    property list<Action> menuActions: [
+        Action {
+            id: audioInputMenuAction
+            text: JamiStrings.selectAudioInputDevice
+            property var listModel: AudioDeviceModel {
+                id: audioInputDeviceListModel
+                lrcInstance: LRCInstance
+                type: AudioDeviceModel.Type.Record
+            }
+            function accept(index) {
+                AvAdapter.stopAudioMeter(false)
+                AVModel.setInputDevice(listModel.data(
+                                       listModel.index(index, 0),
+                                       AudioDeviceModel.RawDeviceName))
+                AvAdapter.startAudioMeter(false)
+            }
+        },
+        Action {
+            id: audioOutputMenuAction
+            text: JamiStrings.selectAudioOutputDevice
+            property var listModel: AudioDeviceModel {
+                id: audioOutputDeviceListModel
+                lrcInstance: LRCInstance
+                type: AudioDeviceModel.Type.Playback
+            }
+            function accept(index) {
+                AvAdapter.stopAudioMeter(false)
+                AVModel.setOutputDevice(listModel.data(
+                                        listModel.index(index, 0),
+                                        AudioDeviceModel.RawDeviceName))
+                AvAdapter.startAudioMeter(false)
+            }
+        },
+        Action {
+            id: videoInputMenuAction
+            text: JamiStrings.selectVideoDevice
+            property var listModel: VideoInputDeviceModel {
+                id: videoInputDeviceListModel
+                lrcInstance: LRCInstance
+            }
+            function accept(index) {
+                if(listModel.deviceCount() < 1)
+                    return
+                try {
+                    var deviceId = listModel.data(
+                                listModel.index(index, 0),
+                                VideoInputDeviceModel.DeviceId)
+                    var deviceName = listModel.data(
+                                listModel.index(index, 0),
+                                VideoInputDeviceModel.DeviceName)
+                    if(deviceId.length === 0) {
+                        console.warn("Couldn't find device: " + deviceName)
+                        return
+                    }
+                    if (AVModel.getCurrentVideoCaptureDevice() !== deviceId) {
+                        AVModel.setCurrentVideoCaptureDevice(deviceId)
+                        AVModel.setDefaultDevice(deviceId)
+                    }
+                    AvAdapter.selectVideoInputDeviceById(deviceId)
+                } catch(err){ console.warn(err.message) }
+            }
+        }
+    ]
+
+    property list<Action> primaryActions: [
+        Action {
+            id: muteAudioAction
+            onTriggered: CallAdapter.muteThisCallToggle()
+            checkable: true
+            icon.source: checked ?
+                             "qrc:/images/icons/mic_off-24px.svg" :
+                             "qrc:/images/icons/mic-24px.svg"
+            icon.color: checked ? "red" : "white"
+            text: !checked ? JamiStrings.mute : JamiStrings.unmute
+            property var menuAction: audioInputMenuAction
+        },
+        Action {
+            id: hangupAction
+            onTriggered: CallAdapter.hangUpThisCall()
+            icon.source: "qrc:/images/icons/ic_call_end_white_24px.svg"
+            icon.color: "white"
+            text: JamiStrings.hangup
+            property bool hasBg: true
+        },
+        Action {
+            id: muteVideoAction
+            onTriggered: CallAdapter.videoPauseThisCallToggle()
+            checkable: true
+            icon.source: checked ?
+                             "qrc:/images/icons/videocam_off-24px.svg" :
+                             "qrc:/images/icons/videocam-24px.svg"
+            icon.color: checked ? "red" : "white"
+            text: !checked ? JamiStrings.pauseVideo : JamiStrings.resumeVideo
+            property var menuAction: videoInputMenuAction
+        }
+    ]
+
+    property list<Action> secondaryActions: [
+        Action {
+            id: audioOutputAction
+            // temp hack for missing back-end, just open device selection
+            property bool bypassMuteAction: true
+            checkable: !bypassMuteAction
+            icon.source: "qrc:/images/icons/spk_black_24dp.svg"
+            icon.color: "white"
+            text: JamiStrings.selectAudioOutputDevice
+            property var menuAction: audioOutputMenuAction
+        },
+        Action {
+            id: addPersonAction
+            onTriggered: root.addToConferenceClicked()
+            icon.source: "qrc:/images/icons/add_people_black_24dp.svg"
+            icon.color: "white"
+            text: JamiStrings.addParticipants
+        },
+        Action {
+            id: chatAction
+            onTriggered: root.chatClicked()
+            icon.source: "qrc:/images/icons/chat_black_24dp.svg"
+            icon.color: "white"
+            text: JamiStrings.chat
+        },
+        Action {
+            id: shareAction
+            onTriggered: root.shareScreenClicked()
+            icon.source: "qrc:/images/icons/share_screen_black_24dp.svg"
+            icon.color: "white"
+            text: JamiStrings.shareScreen
+            property real size: 34
+        },
+        Action {
+            id: recordAction
+            onTriggered: CallAdapter.recordThisCallToggle()
+            checkable: true
+            icon.source: "qrc:/images/icons/record_black_24dp.svg"
+            icon.color: checked ? "red" : "white"
+            text: !checked ? JamiStrings.startRec : JamiStrings.stopRec
+            property bool blinksWhenChecked: true
+            property real size: 28
+        },
+        Action {
+            id: pluginsAction
+            onTriggered: root.pluginsClicked()
+            icon.source: "qrc:/images/icons/plugins-24px.svg"
+            icon.color: "white"
+            text: JamiStrings.viewPlugin
+            enabled: UtilsAdapter.checkShowPluginsButton(true)
+        }
+    ]
+
+    property var overflowItemCount
+
+    Connections {
+        target: callOverlay
+        function onIsAudioOnlyChanged() { reset() }
+        function onIsSIPChanged() { reset() }
+        function onIsModeratorChanged() { reset() }
+    }
+
+    function reset() {
+        CallOverlayModel.clearControls();
+
+        // centered controls
+        CallOverlayModel.addPrimaryControl(muteAudioAction)
+        CallOverlayModel.addPrimaryControl(hangupAction)
+        if (!isAudioOnly)
+            CallOverlayModel.addPrimaryControl(muteVideoAction)
+
+        // overflow controls
+        CallOverlayModel.addSecondaryControl(audioOutputAction)
+        if (isModerator && !isSIP)
+            CallOverlayModel.addSecondaryControl(addPersonAction)
+        CallOverlayModel.addSecondaryControl(chatAction)
+        if (!isAudioOnly)
+            CallOverlayModel.addSecondaryControl(shareAction)
+        CallOverlayModel.addSecondaryControl(recordAction)
+        if (UtilsAdapter.checkShowPluginsButton(true))
+            CallOverlayModel.addSecondaryControl(pluginsAction)
+        overflowItemCount = CallOverlayModel.secondaryModel().rowCount()
+
+        muteAudioAction.checked = isAudioMuted
+        muteVideoAction.checked = isVideoMuted
+    }
+
+    Item {
+        id: centralControls
+        anchors.centerIn: parent
+        width: childrenRect.width
+        height: root.height
+
+        RowLayout {
+            spacing: 0
+
+            ListView {
+                property bool centeredGroup: true
+
+                orientation: ListView.Horizontal
+                implicitWidth: contentWidth
+                implicitHeight: contentHeight
+
+                model: CallOverlayModel.primaryModel()
+                delegate: buttonDelegate
+            }
+        }
+    }
+    Item {
+        id: overflowRect
+        property real remainingSpace: (root.width - centralControls.width) / 2
+        anchors.right: parent.right
+        width: childrenRect.width
+        height: root.height
+
+        RowLayout {
+            spacing: itemSpacing
+
+            ListView {
+                id: overflowItemListView
+
+                orientation: ListView.Horizontal
+                implicitWidth: contentWidth
+                implicitHeight: overflowRect.height
+
+                spacing: itemSpacing
+
+                property int overflowIndex: {
+                    var maxItems = Math.floor((overflowRect.remainingSpace - 24) / root.height) - 1
+                    return Math.min(overflowItemCount, maxItems)
+                }
+                property int nOverflowItems: overflowItemCount - overflowIndex
+                onNOverflowItemsChanged: {
+                    var diff = overflowItemListView.count - nOverflowItems
+                    var effectiveOverflowIndex = overflowIndex
+                    if (effectiveOverflowIndex === overflowItemCount - 1)
+                        effectiveOverflowIndex += diff
+
+                    CallOverlayModel.overflowIndex = effectiveOverflowIndex
+                }
+
+                model: CallOverlayModel.overflowModel()
+                delegate: buttonDelegate
+            }
+            ComboBox {
+                id: overflowButton
+
+                visible: CallOverlayModel.overflowIndex < overflowItemCount
+                width: root.height
+                height: width
+
+                model: CallOverlayModel.overflowHiddenModel()
+
+                delegate: buttonDelegate
+
+                indicator: null
+
+                contentItem: Text {
+                    text: "â‹®"
+                    color: "white"
+                    font.pointSize: 24
+                    font.weight: Font.Bold
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignHCenter
+                }
+
+                background: Rectangle {
+                    implicitWidth: root.height
+                    implicitHeight: implicitWidth
+                    color: overflowButton.down ?
+                               "#80aaaaaa" :
+                               overflowButton.hovered ?
+                                   "#80777777" :
+                                   "#80444444"
+                }
+
+                Item {
+                    implicitHeight: children[0].contentHeight
+                    width: overflowButton.width
+                    anchors.bottom: parent.top
+                    anchors.bottomMargin: itemSpacing
+                    visible: !overflowButton.popup.visible
+                    ListView {
+                        spacing: itemSpacing
+                        anchors.fill: parent
+                        model: CallOverlayModel.overflowVisibleModel()
+                        delegate: buttonDelegate
+                        ScrollIndicator.vertical: ScrollIndicator {}
+
+                        add: Transition {
+                            NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 80 }
+                            NumberAnimation { property: "scale"; from: 0; to: 1.0; duration: 80 }
+                        }
+                    }
+                }
+
+                popup: Popup {
+                    y: overflowButton.height + itemSpacing
+                    width: overflowButton.width
+                    implicitHeight: contentItem.implicitHeight
+                    padding: 0
+
+                    contentItem: ListView {
+                        id: overflowListView
+                        spacing: itemSpacing
+                        implicitHeight: contentHeight
+                        model: overflowButton.popup.visible ?
+                                   overflowButton.delegateModel :
+                                   null
+
+                        ScrollIndicator.vertical: ScrollIndicator {}
+
+                    }
+
+                    background: Rectangle {
+                        color: "transparent"
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/mainview/components/CallButtonDelegate.qml b/src/mainview/components/CallButtonDelegate.qml
new file mode 100644
index 0000000000000000000000000000000000000000..86e0f8e6933f5478151df96939f24ab90080d692
--- /dev/null
+++ b/src/mainview/components/CallButtonDelegate.qml
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2021 by Savoir-faire Linux
+ * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.14
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import QtGraphicalEffects 1.14
+
+import net.jami.Models 1.0
+import net.jami.Constants 1.0
+
+import "../../commoncomponents"
+
+ItemDelegate {
+    id: wrapper
+
+    property bool isFirst: index < 1
+    property bool isLast: index + 1 < ListView.view.count ? false : true
+    property bool hasLast: ListView.view.centeredGroup !== undefined
+    property bool isVertical: wrapper.ListView.view.orientation === ListView.Vertical
+
+    property alias subMenuVisible: menu.popup.visible
+
+    action: ItemAction
+    checkable: ItemAction.checkable
+
+    // hide the action's visual elements like the blurry looking icon
+    icon.source: ""
+    text: ""
+
+    z: index
+
+    // TODO: remove this when output volume control is implemented
+    MouseArea {
+        visible: ItemAction.bypassMuteAction !== undefined &&
+                 ItemAction.bypassMuteAction && !menu.popup.visible
+        anchors.fill: wrapper
+        onClicked: menu.popup.open()
+    }
+
+    background: HalfPill {
+        anchors.fill: parent
+        radius: 5
+        color: {
+            if (supplimentaryBackground.visible)
+                return "#c4272727"
+            return wrapper.down ?
+                        "#c4777777" :
+                        wrapper.hovered && !menu.hovered ?
+                            "#c4444444" :
+                            "#c4272727"
+        }
+        type: {
+            if (isVertical) {
+                if (isFirst)
+                    return HalfPill.Top
+                else if (isLast && hasLast)
+                    return HalfPill.Bottom
+            } else {
+                if (isFirst)
+                    return HalfPill.Left
+                else if (isLast && hasLast)
+                     return HalfPill.Right
+            }
+            return HalfPill.None
+        }
+
+        Behavior on color {
+            ColorAnimation { duration: JamiTheme.shortFadeDuration }
+        }
+    }
+
+    Rectangle {
+        id: supplimentaryBackground
+
+        visible: ItemAction.hasBg !== undefined
+        color: wrapper.down ?
+                   Qt.lighter(JamiTheme.refuseRed, 1.5) :
+                   wrapper.hovered && !menu.hovered ?
+                       JamiTheme.refuseRed :
+                       JamiTheme.refuseRedTransparent
+        anchors.fill: parent
+        radius: width / 2
+
+        Behavior on color {
+            ColorAnimation { duration: JamiTheme.shortFadeDuration }
+        }
+    }
+
+    ResponsiveImage {
+        id: icon
+
+        // TODO: remove this when the icons are size corrected
+        property real size: ItemAction.size !== undefined ?
+                                ItemAction.size : 30
+        containerWidth: size
+        containerHeight: size
+
+        anchors.centerIn: parent
+        horizontalAlignment: Text.AlignHCenter
+        source: ItemAction.icon.source
+        color: ItemAction.icon.color
+
+        SequentialAnimation on opacity {
+            loops: Animation.Infinite
+            running: ItemAction.blinksWhenChecked !== undefined &&
+                     ItemAction.blinksWhenChecked && checked
+            NumberAnimation { from: 1; to: 0; duration: JamiTheme.recordBlinkDuration }
+            NumberAnimation { from: 0; to: 1; duration: JamiTheme.recordBlinkDuration }
+        }
+    }
+
+    // custom anchor for the tooltips
+    Item {
+        anchors.top: !isVertical ? parent.bottom : undefined
+        anchors.topMargin: 25
+        anchors.horizontalCenter: !isVertical ? parent.horizontalCenter : undefined
+
+        anchors.right: isVertical ? parent.left : undefined
+        anchors.rightMargin: isVertical ? toolTip.contentWidth / 2 + 12 : 0
+        anchors.verticalCenter: isVertical ? parent.verticalCenter : undefined
+        anchors.verticalCenterOffset: isVertical ? toolTip.contentHeight / 2 + 4 : 0
+
+        MaterialToolTip {
+            id: toolTip
+            parent: parent
+            visible: text.length > 0 && (wrapper.hovered || menu.hovered)
+            text: menu.hovered ? menuAction.text : ItemAction.text
+            verticalPadding: 1
+            font.pointSize: 9
+        }
+    }
+
+    property var menuAction: ItemAction.menuAction
+
+    ComboBox {
+        id: menu
+
+        indicator: null
+
+        visible: menuAction !== undefined && !BadgeCount
+        anchors.horizontalCenter: parent.horizontalCenter
+        width: 18
+        height: width
+        y: -4
+
+        Connections {
+            target: menuAction !== undefined ?
+                        menuAction :
+                        null
+            function onTriggered() {
+                itemListView.currentIndex =
+                        menuAction.listModel.getCurrentIndex()
+            }
+        }
+
+        contentItem: Text {
+            text: "^"
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            color: "white"
+        }
+
+        background: Rectangle {
+            color: menu.down ?
+                       "#aaaaaa" :
+                       menu.hovered ?
+                           "#777777" :
+                           "#444444"
+            radius: 4
+        }
+
+        onActivated: menuAction.accept(index)
+        model: visible ? menuAction.listModel : null
+        delegate: ItemDelegate {
+            id: menuItem
+
+            width: itemListView.menuItemWidth
+            height: itemListView.menuItemHeight
+            background: Rectangle {
+                anchors.fill: parent
+                color: menuItem.down ?
+                           "#c4aaaaaa" :
+                           menuItem.hovered ?
+                               "#c4777777" :
+                               "transparent"
+            }
+            contentItem: RowLayout {
+                anchors.fill: parent
+                anchors.margins: 6
+                ResponsiveImage {
+                    source: menuItem.ListView.isCurrentItem ?
+                                "qrc:/images/icons/check_box-24px.svg" :
+                                "qrc:/images/icons/check_box_outline_blank-24px.svg"
+                    layer.enabled: true
+                    layer.effect: ColorOverlay { color: "white" }
+                }
+                Text {
+                    Layout.fillWidth: true
+                    horizontalAlignment: Text.AlignLeft
+                    verticalAlignment: Text.AlignVCenter
+                    text: DeviceName
+                    elide: Text.ElideRight
+                    color: "white"
+                }
+            }
+        }
+
+        popup: Popup {
+            id: itemPopup
+
+            y: isVertical ?
+                   -(implicitHeight - wrapper.height) / 2 :
+                   -implicitHeight - 12
+            x: isVertical ?
+                   -implicitWidth - 24 :
+                   -(implicitWidth - wrapper.width) / 2 - 18
+
+            implicitWidth: contentItem.implicitWidth
+            implicitHeight: contentItem.implicitHeight
+            leftPadding: 0
+            rightPadding: 0
+
+            onOpened: menuAction.triggered()
+
+            contentItem: ListView {
+                id: itemListView
+
+                property real menuItemWidth: 0
+                property real menuItemHeight: 39
+
+                orientation: ListView.Vertical
+                implicitWidth: menuItemWidth
+                implicitHeight: Math.min(contentHeight,
+                                         menuItemHeight * 6) + 24
+
+                ScrollIndicator.vertical: ScrollIndicator {}
+
+                clip: true
+
+                model: menu.delegateModel
+
+                TextMetrics { id: itemTextMetrics }
+
+                // recalc list width based on max item width
+                onCountChanged: {
+                    // Hack: use AudioDeviceModel.DeviceName role for video as well
+                    var maxWidth = 0
+                    for (var i = 0; i < count; ++i) {
+                        var idx = menuAction.listModel.index(i, 0)
+                        itemTextMetrics.text = menuAction.listModel.data(
+                                    idx, AudioDeviceModel.DeviceName)
+                        if (itemTextMetrics.boundingRect.width > maxWidth)
+                            maxWidth = itemTextMetrics.boundingRect.width
+                    }
+                    // 30(icon) + 5(layout spacing) + 12(margins)
+                    menuItemWidth = Math.min(256, maxWidth + 30 + 5 + 12)
+                }
+            }
+
+            background: Rectangle {
+                anchors.fill: parent
+                radius: 5
+                color: "#c4272727"
+            }
+        }
+
+        layer.enabled: true
+        layer.effect: DropShadow {
+            z: -1
+            horizontalOffset: 0
+            verticalOffset: 0
+            radius: 8.0
+            samples: 16
+            color: "#80000000"
+        }
+    }
+
+    BadgeNotifier {
+        id: badge
+
+        count: BadgeCount
+        anchors.horizontalCenter: parent.horizontalCenter
+        width: 18
+        height: width
+        radius: 4
+        y: -4
+    }
+}
diff --git a/src/mainview/components/CallOverlay.qml b/src/mainview/components/CallOverlay.qml
index ea5b150fc8b492052de4e77ec5e28268e945294a..8e99e8e9027bac96d2ed4316853ae1d431999e94 100644
--- a/src/mainview/components/CallOverlay.qml
+++ b/src/mainview/components/CallOverlay.qml
@@ -2,7 +2,7 @@
  * Copyright (C) 2020 by Savoir-faire Linux
  * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
  * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
- * Author: Aline Gondim Santos   <aline.gondimsantos@savoirfairelinux.com>
+ * 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
@@ -29,6 +29,8 @@ import net.jami.Adapters 1.0
 import net.jami.Constants 1.0
 
 import "../js/contactpickercreation.js" as ContactPickerCreation
+import "../js/selectscreenwindowcreation.js" as SelectScreenWindowCreation
+import "../js/screenrubberbandcreation.js" as ScreenRubberBandCreation
 import "../js/pluginhandlerpickercreation.js" as PluginHandlerPickerCreation
 
 import "../../commoncomponents"
@@ -37,13 +39,18 @@ Item {
     id: root
 
     property alias participantsLayer: __participantsLayer
-    property string timeText: "00:00"
-    property string remoteRecordingLabel: ""
-    property bool isVideoMuted: true
-    property bool isAudioOnly: false
+
+    property bool isPaused
+    property bool isAudioOnly
+    property bool isAudioMuted
+    property bool isVideoMuted
+    property bool isRecording
+    property bool isSIP
+    property bool isModerator
+
     property string bestName: ""
 
-    signal overlayChatButtonClicked
+    signal chatButtonClicked
 
     onVisibleChanged: if (!visible) callViewContextMenu.close()
 
@@ -54,25 +61,23 @@ Item {
 
     function setRecording(localIsRecording) {
         callViewContextMenu.localIsRecording = localIsRecording
-        recordingRect.visible = localIsRecording
+        mainOverlay.recordingVisible = localIsRecording
                 || callViewContextMenu.peerIsRecording
     }
 
-    function updateButtonStatus(isPaused, isAudioOnly, isAudioMuted, isVideoMuted,
-                                isRecording, isSIP, isConferenceCall) {
-        root.isVideoMuted = isVideoMuted
-        callViewContextMenu.isSIP = isSIP
-        callViewContextMenu.isPaused = isPaused
-        callViewContextMenu.isAudioOnly = isAudioOnly
-        callViewContextMenu.localIsRecording = isRecording
-        recordingRect.visible = isRecording
-        callOverlayButtonGroup.setButtonStatus(isPaused, isAudioOnly,
-                                               isAudioMuted, isVideoMuted,
-                                               isSIP, isConferenceCall)
-    }
+    function updateUI(isPaused, isAudioOnly, isAudioMuted,
+                      isVideoMuted, isRecording, isSIP) {
+        if (isPaused !== undefined) {
+            root.isPaused = isPaused
+            root.isAudioOnly = isAudioOnly
+            root.isAudioMuted = isAudioMuted
+            root.isVideoMuted = isVideoMuted
+            root.isRecording = isRecording
+            root.isSIP = isSIP
+            mainOverlay.recordingVisible = isRecording
+        }
 
-    function updateMenu() {
-        callOverlayButtonGroup.updateMenu()
+        root.isModerator = CallAdapter.isCurrentModerator()
     }
 
     function showOnHoldImage(visible) {
@@ -109,17 +114,19 @@ Item {
                                               : JamiStrings.isRecording)
         }
 
-        remoteRecordingLabel = state? label : JamiStrings.peerStoppedRecording
+        mainOverlay.remoteRecordingLabel = state ?
+                    label :
+                    JamiStrings.peerStoppedRecording
         callViewContextMenu.peerIsRecording = state
-        recordingRect.visible = callViewContextMenu.localIsRecording
+        mainOverlay.recordingVisible = callViewContextMenu.localIsRecording
                 || callViewContextMenu.peerIsRecording
         callOverlayRectMouseArea.entered()
     }
 
     function resetRemoteRecording() {
-        remoteRecordingLabel = ""
+        mainOverlay.remoteRecordingLabel = ""
         callViewContextMenu.peerIsRecording = false
-        recordingRect.visible = callViewContextMenu.localIsRecording
+        mainOverlay.recordingVisible = callViewContextMenu.localIsRecording
     }
 
     SipInputPanel {
@@ -143,182 +150,61 @@ Item {
         source: "qrc:/images/icons/ic_pause_white_100px.svg"
     }
 
-    Item {
-        id: mainOverlay
-
-        anchors.fill: parent
-        opacity: 0
-
-        // (un)subscribe to an app-wide mouse move event trap filtered
-        // for the overlay's geometry
-        onVisibleChanged: visible ?
-                              CallOverlayModel.registerFilter(appWindow, this) :
-                              CallOverlayModel.unregisterFilter(appWindow, this)
-
-        Connections {
-            target: CallOverlayModel
-
-            function onMouseMoved(item) {
-                if (item === mainOverlay) {
-                    mainOverlay.opacity = 1
-                    fadeOutTimer.restart()
-                }
-            }
-        }
-
-        // control overlay fade out.
-        Timer {
-            id: fadeOutTimer
-            interval: JamiTheme.overlayFadeDelay
-            onTriggered: {
-                if (callOverlayButtonGroup.hovered)
-                    return
-                mainOverlay.opacity = 0
-                resetLabelsTimer.restart()
-            }
-        }
+    function openContactPicker(type) {
+        ContactPickerCreation.createContactPickerObjects(type, root)
+        ContactPickerCreation.openContactPicker()
+    }
 
-        // Timer to reset recording label and call duration time
-        Timer {
-            id: resetLabelsTimer
-            interval: 1000
-            running: root.visible
-            repeat: true
-            onTriggered: {
-                timeText = CallAdapter.getCallDurationTime(LRCInstance.currentAccountId,
-                                                           LRCInstance.selectedConvUid)
-                if (mainOverlay.opacity === 0 && !callViewContextMenu.peerIsRecording)
-                    remoteRecordingLabel = ""
-            }
+    function openShareScreen() {
+        if (Qt.application.screens.length === 1) {
+            AvAdapter.shareEntireScreen(0)
+        } else {
+            SelectScreenWindowCreation.createSelectScreenWindowObject()
+            SelectScreenWindowCreation.showSelectScreenWindow()
         }
+    }
 
-        Item {
-            id: overlayUpperPartRect
-
-            anchors.top: parent.top
-
-            width: parent.width
-            height: 50
-
-            RowLayout {
-                anchors.fill: parent
-
-                Text {
-                    id: jamiBestNameText
-
-                    Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
-                    Layout.preferredWidth: overlayUpperPartRect.width / 3
-                    Layout.preferredHeight: 50
-                    leftPadding: 16
-
-                    font.pointSize: JamiTheme.textFontSize
-
-                    horizontalAlignment: Text.AlignLeft
-                    verticalAlignment: Text.AlignVCenter
-
-                    text: textMetricsjamiBestNameText.elidedText
-                    color: "white"
-
-                    TextMetrics {
-                        id: textMetricsjamiBestNameText
-                        font: jamiBestNameText.font
-                        text: {
-                            if (!root.isAudioOnly) {
-                                if (remoteRecordingLabel === "") {
-                                    return root.bestName
-                                } else {
-                                    return remoteRecordingLabel
-                                }
-                            }
-                            return ""
-                        }
-                        elideWidth: overlayUpperPartRect.width / 3
-                        elide: Qt.ElideRight
-                    }
-                }
-
-                Text {
-                    id: callTimerText
-                    Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
-                    Layout.preferredWidth: 64
-                    Layout.minimumWidth: 64
-                    Layout.preferredHeight: 48
-                    Layout.rightMargin: recordingRect.visible?
-                                            0 : JamiTheme.preferredMarginSize
-                    font.pointSize: JamiTheme.textFontSize
-                    horizontalAlignment: Text.AlignRight
-                    verticalAlignment: Text.AlignVCenter
-                    text: textMetricscallTimerText.elidedText
-                    color: "white"
-                    TextMetrics {
-                        id: textMetricscallTimerText
-                        font: callTimerText.font
-                        text: timeText
-                        elideWidth: overlayUpperPartRect.width / 4
-                        elide: Qt.ElideRight
-                    }
-                }
-
-                Rectangle {
-                    id: recordingRect
-                    Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
-                    Layout.rightMargin: JamiTheme.preferredMarginSize
-                    height: 16
-                    width: 16
-                    radius: height / 2
-                    color: "red"
-
-                    SequentialAnimation on color {
-                        loops: Animation.Infinite
-                        running: true
-                        ColorAnimation { from: "red"; to: "transparent";  duration: 500 }
-                        ColorAnimation { from: "transparent"; to: "red"; duration: 500 }
-                    }
-                }
-            }
+    function openShareScreenArea() {
+        if (Qt.platform.os !== "windows") {
+            AvAdapter.shareScreenArea(0, 0, 0, 0)
+        } else {
+            ScreenRubberBandCreation.createScreenRubberBandWindowObject()
+            ScreenRubberBandCreation.showScreenRubberBandWindow()
         }
+    }
 
-        CallOverlayButtonGroup {
-            id: callOverlayButtonGroup
+    function openPluginsMenu() {
+        PluginHandlerPickerCreation.createPluginHandlerPickerObjects(root, true)
+        PluginHandlerPickerCreation.openPluginHandlerPicker()
+    }
 
-            anchors.bottom: parent.bottom
-            anchors.bottomMargin: 10
-            anchors.horizontalCenter: parent.horizontalCenter
+    MainOverlay {
+        id: mainOverlay
 
-            height: 56
-            width: root.width
+        anchors.fill: parent
 
-            onChatButtonClicked: {
-                root.overlayChatButtonClicked()
-            }
+        bestName: root.bestName
 
-            onAddToConferenceButtonClicked: {
-                // Create contact picker - conference.
-                ContactPickerCreation.createContactPickerObjects(
-                            ContactList.CONFERENCE,
-                            root)
-                ContactPickerCreation.openContactPicker()
-            }
+        Connections {
+            target: mainOverlay.callActionBar
+            function onChatClicked() { root.chatButtonClicked() }
+            function onAddToConferenceClicked() { openContactPicker(ContactList.CONFERENCE) }
+            function onTransferClicked() { openContactPicker(ContactList.TRANSFER) }
+            function onShareScreenClicked() { openShareScreen() }
+            function onShareScreenAreaClicked() { openShareScreenArea() }
+            function onPluginsClicked() { openPluginsMenu() }
         }
-
-        Behavior on opacity { NumberAnimation { duration: JamiTheme.overlayFadeDuration }}
     }
 
     CallViewContextMenu {
         id: callViewContextMenu
 
-        onTransferCallButtonClicked: {
-            // Create contact picker - sip transfer.
-            ContactPickerCreation.createContactPickerObjects(
-                        ContactList.TRANSFER,
-                        root)
-            ContactPickerCreation.openContactPicker()
-        }
+        isSIP: root.isSIP
+        isPaused: root.isPaused
+        isAudioOnly: root.isAudioOnly
+        localIsRecording: root.isRecording
 
-        onPluginItemClicked: {
-            // Create plugin handler picker - PLUGINS
-            PluginHandlerPickerCreation.createPluginHandlerPickerObjects(root, true)
-            PluginHandlerPickerCreation.openPluginHandlerPicker()
-        }
+        onTransferCallButtonClicked: openContactPicker(ContactList.TRANSFER)
+        onPluginItemClicked: openPluginsMenu()
     }
 }
diff --git a/src/mainview/components/CallOverlayButtonGroup.qml b/src/mainview/components/CallOverlayButtonGroup.qml
deleted file mode 100644
index 4783fb1d40aab295478cdb769fd87f8c68838204..0000000000000000000000000000000000000000
--- a/src/mainview/components/CallOverlayButtonGroup.qml
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2020 by Savoir-faire Linux
- * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
- * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <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 QtQml 2.14
-
-import net.jami.Adapters 1.0
-import net.jami.Models 1.0
-import net.jami.Constants 1.0
-
-import "../../commoncomponents"
-
-Control {
-    id: root
-
-    // ButtonCounts here is to make sure that flow layout margin is calculated correctly,
-    // since no other methods can make buttons at the layout center.
-    property var isModerator: true
-    property var isSip: false
-
-    signal chatButtonClicked
-    signal addToConferenceButtonClicked
-
-    function updateMenu() {
-        root.isModerator = CallAdapter.isCurrentModerator()
-        addToConferenceButton.visible = !root.isSip && root.isModerator
-    }
-
-    function setButtonStatus(isPaused, isAudioOnly, isAudioMuted, isVideoMuted,
-                             isSIP, isConferenceCall) {
-        root.isModerator = CallAdapter.isCurrentModerator()
-        root.isSip = isSIP
-        noVideoButton.visible = !isAudioOnly
-        addToConferenceButton.visible = !root.isSIP && root.isModerator
-
-        noMicButton.checked = isAudioMuted
-        noVideoButton.checked = isVideoMuted
-    }
-
-    z: 2
-
-    RowLayout {
-        id: callOverlayButtonGroup
-
-        spacing: 8
-        height: 56
-
-        anchors.fill: parent
-
-        Item {
-            Layout.preferredWidth: {
-                // TODO: refactor with Flow if possible
-                // 6 is the number of button
-                // If ~ 500px, go into wide mode
-                if (callOverlayButtonGroup.width < (JamiTheme.callButtonPreferredSize * 6 -
-                        callOverlayButtonGroup.spacing * 6 + 300)) {
-                    return 0
-                } else {
-                    return  callOverlayButtonGroup.width / 2 - JamiTheme.callButtonPreferredSize * 1.5 -
-                            callOverlayButtonGroup.spacing
-                }
-            }
-        }
-
-        PushButton {
-            id: noMicButton
-
-            Layout.leftMargin: 8
-            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
-            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
-
-            pressedColor: JamiTheme.invertedPressedButtonColor
-            hoveredColor: JamiTheme.invertedHoveredButtonColor
-            normalColor: JamiTheme.invertedNormalButtonColor
-
-            normalImageSource: "qrc:/images/icons/mic-24px.svg"
-            imageColor: JamiTheme.whiteColor
-            checkable: true
-            checkedImageSource: "qrc:/images/icons/mic_off-24px.svg"
-            checkedImageColor: JamiTheme.declineButtonPressedRed
-
-            toolTipText: !checked ? JamiStrings.mute : JamiStrings.unmute
-
-            onClicked: CallAdapter.muteThisCallToggle()
-        }
-
-        PushButton {
-            id: hangUpButton
-
-            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
-            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
-
-            pressedColor: JamiTheme.declineButtonPressedRed
-            hoveredColor: JamiTheme.declineButtonHoverRed
-            normalColor: JamiTheme.declineButtonRed
-
-            source: "qrc:/images/icons/ic_call_end_white_24px.svg"
-            imageColor: JamiTheme.whiteColor
-
-            toolTipText: JamiStrings.hangup
-
-            onClicked: CallAdapter.hangUpThisCall()
-        }
-
-        PushButton {
-            id: noVideoButton
-
-            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
-            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
-
-            pressedColor: JamiTheme.invertedPressedButtonColor
-            hoveredColor: JamiTheme.invertedHoveredButtonColor
-            normalColor: JamiTheme.invertedNormalButtonColor
-
-            normalImageSource: "qrc:/images/icons/videocam-24px.svg"
-            imageColor: JamiTheme.whiteColor
-            checkable: true
-            checkedImageSource: "qrc:/images/icons/videocam_off-24px.svg"
-            checkedImageColor: JamiTheme.declineButtonPressedRed
-
-            toolTipText: !checked ? JamiStrings.pauseVideo : JamiStrings.resumeVideo
-
-            onClicked: CallAdapter.videoPauseThisCallToggle()
-        }
-
-        Item {
-            Layout.fillWidth: true
-        }
-
-        PushButton {
-            id: addToConferenceButton
-
-            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
-            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
-            visible: !isModerator && !isSip
-
-            pressedColor: JamiTheme.invertedPressedButtonColor
-            hoveredColor: JamiTheme.invertedHoveredButtonColor
-            normalColor: JamiTheme.invertedNormalButtonColor
-
-            source: "qrc:/images/icons/group_add-24px.svg"
-            imageColor: JamiTheme.whiteColor
-
-            toolTipText: JamiStrings.addParticipants
-
-            onClicked: root.addToConferenceButtonClicked()
-        }
-
-        PushButton {
-            id: chatButton
-
-            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
-            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
-
-            pressedColor: JamiTheme.invertedPressedButtonColor
-            hoveredColor: JamiTheme.invertedHoveredButtonColor
-            normalColor: JamiTheme.invertedNormalButtonColor
-
-            source: "qrc:/images/icons/chat-24px.svg"
-            imageColor: JamiTheme.whiteColor
-
-            toolTipText: JamiStrings.chat
-
-            onClicked: root.chatButtonClicked()
-        }
-
-        PushButton {
-            id: optionsButton
-
-            Layout.preferredWidth: JamiTheme.callButtonPreferredSize
-            Layout.preferredHeight: JamiTheme.callButtonPreferredSize
-            Layout.rightMargin: 8
-
-            pressedColor: JamiTheme.invertedPressedButtonColor
-            hoveredColor: JamiTheme.invertedHoveredButtonColor
-            normalColor: JamiTheme.invertedNormalButtonColor
-
-            source: "qrc:/images/icons/more_vert-24px.svg"
-            imageColor: JamiTheme.whiteColor
-
-            toolTipText: JamiStrings.moreOptions
-
-            onClicked: {
-                var rectPos = mapToItem(callStackViewWindow, optionsButton.x, optionsButton.y)
-                callViewContextMenu.x = rectPos.x + optionsButton.width / 2
-                        - callViewContextMenu.width / 2
-                callViewContextMenu.y = rectPos.y - 12 - callViewContextMenu.height
-                callViewContextMenu.openMenu()
-            }
-        }
-    }
-}
diff --git a/src/mainview/components/MainOverlay.qml b/src/mainview/components/MainOverlay.qml
new file mode 100644
index 0000000000000000000000000000000000000000..fd9632a81823ac17af2cc0530186479c3e9179e5
--- /dev/null
+++ b/src/mainview/components/MainOverlay.qml
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2020-2021 by Savoir-faire Linux
+ * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
+ * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
+ * Author: Aline Gondim Santos <aline.gondimsantos@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
+ * 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 QtQml 2.14
+
+import net.jami.Models 1.0
+import net.jami.Adapters 1.0
+import net.jami.Constants 1.0
+
+import "../../commoncomponents"
+
+Item {
+    id: root
+
+    property string timeText: "00:00"
+    property string remoteRecordingLabel: ""
+
+    property string bestName: ""
+
+    property alias recordingVisible: recordingRect.visible
+    property alias callActionBar: __callActionBar
+
+    property bool frozen: callActionBar.overflowOpen ||
+                          callActionBar.hovered ||
+                          callActionBar.subMenuOpen
+
+    opacity: 0
+
+    // (un)subscribe to an app-wide mouse move event trap filtered
+    // for the overlay's geometry
+    onVisibleChanged: visible ?
+                          CallOverlayModel.registerFilter(appWindow, this) :
+                          CallOverlayModel.unregisterFilter(appWindow, this)
+
+    Connections {
+        target: CallOverlayModel
+
+        function onMouseMoved(item) {
+            if (item === root) {
+                root.opacity = 1
+                fadeOutTimer.restart()
+            }
+        }
+    }
+
+    // control overlay fade out.
+    Timer {
+        id: fadeOutTimer
+        interval: JamiTheme.overlayFadeDelay
+        onTriggered: {
+            if (frozen)
+                return
+            root.opacity = 0
+            resetLabelsTimer.restart()
+        }
+    }
+
+    // Timer to reset recording label and call duration time
+    Timer {
+        id: resetLabelsTimer
+        interval: 1000
+        running: root.visible
+        repeat: true
+        onTriggered: {
+            root.timeText = CallAdapter.getCallDurationTime(
+                        LRCInstance.currentAccountId,
+                        LRCInstance.selectedConvUid)
+            if (root.opacity === 0 && !callViewContextMenu.peerIsRecording)
+                root.remoteRecordingLabel = ""
+        }
+    }
+
+    Item {
+        id: overlayUpperPartRect
+
+        anchors.top: parent.top
+
+        width: parent.width
+        height: 50
+
+        RowLayout {
+            anchors.fill: parent
+
+            Text {
+                id: jamiBestNameText
+
+                Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
+                Layout.preferredWidth: overlayUpperPartRect.width / 3
+                Layout.preferredHeight: 50
+                leftPadding: 16
+
+                font.pointSize: JamiTheme.textFontSize
+
+                horizontalAlignment: Text.AlignLeft
+                verticalAlignment: Text.AlignVCenter
+
+                text: textMetricsjamiBestNameText.elidedText
+                color: "white"
+
+                TextMetrics {
+                    id: textMetricsjamiBestNameText
+                    font: jamiBestNameText.font
+                    text: {
+                        if (!root.isAudioOnly) {
+                            if (remoteRecordingLabel === "") {
+                                return root.bestName
+                            } else {
+                                return remoteRecordingLabel
+                            }
+                        }
+                        return ""
+                    }
+                    elideWidth: overlayUpperPartRect.width / 3
+                    elide: Qt.ElideRight
+                }
+            }
+
+            Text {
+                id: callTimerText
+                Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
+                Layout.preferredWidth: 64
+                Layout.minimumWidth: 64
+                Layout.preferredHeight: 48
+                Layout.rightMargin: recordingRect.visible?
+                                        0 : JamiTheme.preferredMarginSize
+                font.pointSize: JamiTheme.textFontSize
+                horizontalAlignment: Text.AlignRight
+                verticalAlignment: Text.AlignVCenter
+                text: textMetricscallTimerText.elidedText
+                color: "white"
+                TextMetrics {
+                    id: textMetricscallTimerText
+                    font: callTimerText.font
+                    text: timeText
+                    elideWidth: overlayUpperPartRect.width / 4
+                    elide: Qt.ElideRight
+                }
+            }
+
+            Rectangle {
+                id: recordingRect
+                Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
+                Layout.rightMargin: JamiTheme.preferredMarginSize
+                height: 16
+                width: 16
+                radius: height / 2
+                color: "red"
+
+                SequentialAnimation on color {
+                    loops: Animation.Infinite
+                    running: true
+                    ColorAnimation {
+                        from: "red"; to: "transparent";
+                        duration: JamiTheme.recordBlinkDuration
+                    }
+                    ColorAnimation {
+                        from: "transparent"; to: "red";
+                        duration: JamiTheme.recordBlinkDuration
+                    }
+                }
+            }
+        }
+    }
+
+    CallActionBar {
+        id: __callActionBar
+
+        anchors {
+            bottom: parent.bottom
+            bottomMargin: 26
+        }
+
+        width: parent.width
+        height: 55
+    }
+
+    Behavior on opacity { NumberAnimation { duration: JamiTheme.overlayFadeDuration }}
+}
diff --git a/src/mainview/components/OngoingCallPage.qml b/src/mainview/components/OngoingCallPage.qml
index 45bfbc0ef617e834e9bf9243c6315d75880e16ac..05584aa9dbc2ef3c46215537f45e3bf11ba45556 100644
--- a/src/mainview/components/OngoingCallPage.qml
+++ b/src/mainview/components/OngoingCallPage.qml
@@ -302,15 +302,11 @@ Rectangle  {
                         target: CallAdapter
 
                         function onUpdateOverlay(isPaused, isAudioOnly, isAudioMuted, isVideoMuted,
-                                                 isRecording, isSIP, isConferenceCall, bestName) {
+                                                 isRecording, isSIP, bestName) {
                             callOverlay.showOnHoldImage(isPaused)
                             audioCallPageRectCentralRect.visible = !isPaused && root.isAudioOnly
-                            callOverlay.updateButtonStatus(isPaused,
-                                                                isAudioOnly,
-                                                                isAudioMuted,
-                                                                isVideoMuted,
-                                                                isRecording, isSIP,
-                                                                isConferenceCall)
+                            callOverlay.updateUI(isPaused, isAudioOnly, isAudioMuted, isVideoMuted,
+                                                 isRecording, isSIP)
                             root.bestName = bestName
                             callOverlay.participantsLayer.update(CallAdapter.getConferencesInfos())
                         }
@@ -329,7 +325,7 @@ Rectangle  {
                         }
                     }
 
-                    onOverlayChatButtonClicked: {
+                    onChatButtonClicked: {
                         inCallMessageWebViewStack.visible ?
                                     closeInCallConversation() :
                                     openInCallConversation()
diff --git a/src/mainview/components/ParticipantsLayer.qml b/src/mainview/components/ParticipantsLayer.qml
index 5a15616e702865eb7129f1ae575a81701d28ba48..6b39c40934d9d59bb35c89b58e78c7562459d01d 100644
--- a/src/mainview/components/ParticipantsLayer.qml
+++ b/src/mainview/components/ParticipantsLayer.qml
@@ -40,7 +40,7 @@ Item {
         // TODO: in the future the conference layout should be entirely managed by the client
         // Hack: truncate and ceil participant's overlay position and size to correct
         // when they are not exacts
-        callOverlay.updateMenu()
+        callOverlay.updateUI()
         var showMax = false
         var showMin = false
 
diff --git a/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml b/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml
index 85754663f1a3b2fba20741c2d1651d698f76b3a7..985c0dbc23ed433734d6020b569085bb5b191464 100644
--- a/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml
+++ b/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml
@@ -50,6 +50,6 @@ GeneralMenuItem {
 
     onClicked: {
         var deviceName = videoCallPageContextMenuDeviceItem.itemName
-        AvAdapter.onVideoContextMenuDeviceItemClicked(deviceName)
+        AvAdapter.selectVideoInputDeviceByName(deviceName)
     }
 }
diff --git a/src/settingsview/components/VideoSettings.qml b/src/settingsview/components/VideoSettings.qml
index cfa38815b198c518a3522b5f07e399e43770c254..4343d39f85e62d69230361d9eef5d587711aa978 100644
--- a/src/settingsview/components/VideoSettings.qml
+++ b/src/settingsview/components/VideoSettings.qml
@@ -63,7 +63,7 @@ ColumnLayout {
         }
 
         deviceComboBoxSetting.setCurrentIndex(
-                    deviceComboBoxSetting.comboModel.getCurrentSettingIndex(), true)
+                    deviceComboBoxSetting.comboModel.getCurrentIndex(), true)
         hardwareAccelControl.checked = AVModel.getHardwareAcceleration()
     }
 
diff --git a/src/videoinputdevicemodel.cpp b/src/videoinputdevicemodel.cpp
index b92c19b524f12bfda6c5a9d11db1add9313f0266..694becbfd246351b733d85d5bc4a809a6273223a 100644
--- a/src/videoinputdevicemodel.cpp
+++ b/src/videoinputdevicemodel.cpp
@@ -97,6 +97,8 @@ VideoInputDeviceModel::data(const QModelIndex& index, int role) const
         return QVariant((QString) currentDeviceSetting.size);
     case Role::DeviceName_UTF8:
         return QVariant(currentDeviceSetting.name.toUtf8());
+    case Role::isCurrent:
+        return QVariant(index.row() == getCurrentIndex());
     }
     return QVariant();
 }
@@ -111,6 +113,7 @@ VideoInputDeviceModel::roleNames() const
     roles[CurrentFrameRate] = "CurrentFrameRate";
     roles[CurrentResolution] = "CurrentResolution";
     roles[DeviceName_UTF8] = "DeviceName_UTF8";
+    roles[isCurrent] = "isCurrent";
     return roles;
 }
 
@@ -159,15 +162,9 @@ VideoInputDeviceModel::deviceCount()
 }
 
 int
-VideoInputDeviceModel::getCurrentSettingIndex()
+VideoInputDeviceModel::getCurrentIndex() const
 {
     QString currentId = lrcInstance_->avModel().getCurrentVideoCaptureDevice();
     auto resultList = match(index(0, 0), DeviceId, QVariant(currentId));
-
-    int resultRowIndex = 0;
-    if (resultList.size() > 0) {
-        resultRowIndex = resultList[0].row();
-    }
-
-    return resultRowIndex;
+    return resultList.size() > 0 ? resultList[0].row() : 0;
 }
diff --git a/src/videoinputdevicemodel.h b/src/videoinputdevicemodel.h
index 9278a1b6752901ac618da8260da7ca5c684f1c9e..ed9d42d263bedeb4e4f1eee8a66058bc8f6d6ffd 100644
--- a/src/videoinputdevicemodel.h
+++ b/src/videoinputdevicemodel.h
@@ -25,12 +25,13 @@ class VideoInputDeviceModel : public AbstractListModelBase
     Q_OBJECT
 public:
     enum Role {
-        DeviceChannel = Qt::UserRole + 1,
-        DeviceName,
+        DeviceName = Qt::UserRole + 1,
+        DeviceChannel,
         DeviceId,
         CurrentFrameRate,
         CurrentResolution,
-        DeviceName_UTF8
+        DeviceName_UTF8,
+        isCurrent
     };
     Q_ENUM(Role)
 
@@ -59,8 +60,7 @@ public:
      * This function is to reset the model when there's new account added.
      */
     Q_INVOKABLE int deviceCount();
-    /*
-     * This function is to get the current device id in the demon.
-     */
-    Q_INVOKABLE int getCurrentSettingIndex();
+
+    // get model index of the current device
+    Q_INVOKABLE int getCurrentIndex() const;
 };