From 5dc002197d01cd499839dd34a664867ed24af992 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Thu, 12 Jan 2023 16:33:28 +0100 Subject: [PATCH] Display media captures files (recordings) --- CHANGELOG.md | 1 + linphone-app/CMakeLists.txt | 6 + .../assets/images/recordings_custom.svg | 85 +++++ linphone-app/assets/languages/da.ts | 28 ++ linphone-app/assets/languages/de.ts | 28 ++ linphone-app/assets/languages/en.ts | 28 ++ linphone-app/assets/languages/es.ts | 28 ++ linphone-app/assets/languages/fr_FR.ts | 28 ++ linphone-app/assets/languages/hu.ts | 28 ++ linphone-app/assets/languages/it.ts | 28 ++ linphone-app/assets/languages/ja.ts | 28 ++ linphone-app/assets/languages/lt.ts | 28 ++ linphone-app/assets/languages/pt_BR.ts | 28 ++ linphone-app/assets/languages/ru.ts | 28 ++ linphone-app/assets/languages/sv.ts | 28 ++ linphone-app/assets/languages/tr.ts | 28 ++ linphone-app/assets/languages/uk.ts | 28 ++ linphone-app/assets/languages/zh_CN.ts | 28 ++ linphone-app/resources.qrc | 3 + linphone-app/src/app/App.cpp | 2 + .../src/app/proxyModel/ProxyListModel.hpp | 22 +- linphone-app/src/components/Components.hpp | 3 + .../src/components/call/CallModel.cpp | 34 +- .../src/components/call/CallModel.hpp | 6 +- linphone-app/src/components/camera/Camera.cpp | 34 ++ linphone-app/src/components/camera/Camera.hpp | 12 +- .../src/components/file/FileMediaModel.cpp | 151 +++++++++ .../src/components/file/FileMediaModel.hpp | 75 +++++ .../other/colors/ColorListModel.hpp | 11 + .../src/components/recorder/RecorderModel.cpp | 17 + .../src/components/recorder/RecorderModel.hpp | 3 + .../recorder/RecordingListModel.cpp | 78 +++++ .../recorder/RecordingListModel.hpp | 45 +++ .../recorder/RecordingProxyModel.cpp | 60 ++++ .../recorder/RecordingProxyModel.hpp | 43 +++ .../components/sound-player/SoundPlayer.cpp | 37 ++- .../components/sound-player/SoundPlayer.hpp | 19 +- .../ui/modules/Linphone/Camera/CameraItem.qml | 2 + .../ui/modules/Linphone/Camera/CameraView.qml | 1 + linphone-app/ui/views/App/Main/MainWindow.qml | 13 +- .../ui/views/App/Main/MainWindowMenuBar.qml | 13 + .../views/App/Main/MainWindowTopMenuBar.qml | 10 +- linphone-app/ui/views/App/Main/Recordings.qml | 291 ++++++++++++++++++ .../views/App/Styles/Main/MainWindowStyle.qml | 6 + .../views/App/Styles/Main/RecordingsStyle.qml | 80 +++++ linphone-app/ui/views/App/Styles/qmldir | 1 + linphone-sdk | 2 +- 47 files changed, 1558 insertions(+), 28 deletions(-) create mode 100644 linphone-app/assets/images/recordings_custom.svg create mode 100644 linphone-app/src/components/file/FileMediaModel.cpp create mode 100644 linphone-app/src/components/file/FileMediaModel.hpp create mode 100644 linphone-app/src/components/recorder/RecordingListModel.cpp create mode 100644 linphone-app/src/components/recorder/RecordingListModel.hpp create mode 100644 linphone-app/src/components/recorder/RecordingProxyModel.cpp create mode 100644 linphone-app/src/components/recorder/RecordingProxyModel.hpp create mode 100644 linphone-app/ui/views/App/Main/Recordings.qml create mode 100644 linphone-app/ui/views/App/Styles/Main/RecordingsStyle.qml diff --git a/CHANGELOG.md b/CHANGELOG.md index f4bfa9b0d..81aec64b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Resizeable on mouse's wheel. * Reset on mouse's right click (first for size if changed, second for position) - Hide the active speaker from the mini views. +- Display recordings list from the burger menu. ### Fixed - Mini views layout on actives speaker. diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt index b0231492a..9f6fe0d5a 100644 --- a/linphone-app/CMakeLists.txt +++ b/linphone-app/CMakeLists.txt @@ -212,6 +212,7 @@ set(SOURCES src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp src/components/file/FileDownloader.cpp src/components/file/FileExtractor.cpp + src/components/file/FileMediaModel.cpp src/components/friend/FriendListListener.cpp src/components/history/HistoryModel.cpp src/components/history/HistoryProxyModel.cpp @@ -246,6 +247,8 @@ set(SOURCES src/components/presence/Presence.cpp src/components/recorder/RecorderManager.cpp src/components/recorder/RecorderModel.cpp + src/components/recorder/RecordingListModel.cpp + src/components/recorder/RecordingProxyModel.cpp src/components/search/SearchListener.cpp src/components/search/SearchResultModel.cpp src/components/search/SearchSipAddressesModel.cpp @@ -348,6 +351,7 @@ set(HEADERS src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp src/components/file/FileDownloader.hpp src/components/file/FileExtractor.hpp + src/components/file/FileMediaModel.hpp src/components/friend/FriendListListener.hpp src/components/history/HistoryModel.hpp src/components/history/HistoryProxyModel.hpp @@ -383,6 +387,8 @@ set(HEADERS src/components/presence/Presence.hpp src/components/recorder/RecorderManager.hpp src/components/recorder/RecorderModel.hpp + src/components/recorder/RecordingListModel.hpp + src/components/recorder/RecordingProxyModel.hpp src/components/search/SearchListener.hpp src/components/search/SearchResultModel.hpp src/components/search/SearchSipAddressesModel.hpp diff --git a/linphone-app/assets/images/recordings_custom.svg b/linphone-app/assets/images/recordings_custom.svg new file mode 100644 index 000000000..17be9e8df --- /dev/null +++ b/linphone-app/assets/images/recordings_custom.svg @@ -0,0 +1,85 @@ + + + + + menu_recordings@3x + + + + + + + + + + + + + menu_recordings@3x + + + + diff --git a/linphone-app/assets/languages/da.ts b/linphone-app/assets/languages/da.ts index ef140d3c4..b97fc3918 100644 --- a/linphone-app/assets/languages/da.ts +++ b/linphone-app/assets/languages/da.ts @@ -1792,6 +1792,11 @@ Klik her: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Tjek for opdateringer + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1812,6 +1817,11 @@ Klik her: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Tjek for opdateringer + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2292,6 +2302,24 @@ Klik her: <a href="%1">%1</a> + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/de.ts b/linphone-app/assets/languages/de.ts index a1b2b06b7..a227f29d9 100644 --- a/linphone-app/assets/languages/de.ts +++ b/linphone-app/assets/languages/de.ts @@ -1792,6 +1792,11 @@ Klicken Sie hier: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Auf Aktualisierungen prüfen + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1812,6 +1817,11 @@ Klicken Sie hier: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Auf Aktualisierungen prüfen + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2292,6 +2302,24 @@ Klicken Sie hier: <a href="%1">%1</a> Automatisch + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/en.ts b/linphone-app/assets/languages/en.ts index 5f69dff3a..2b6e9ea7c 100644 --- a/linphone-app/assets/languages/en.ts +++ b/linphone-app/assets/languages/en.ts @@ -1792,6 +1792,11 @@ Click here: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Check for updates + + recordings + 'Recordings' : Label for the recordings menu. + Recordings + MainWindowTopMenuBar @@ -1812,6 +1817,11 @@ Click here: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Check for updates + + recordings + 'Recordings' : Label for the recordings menu. + Recordings + ManageAccounts @@ -2292,6 +2302,24 @@ Click here: <a href="%1">%1</a> Auto + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + No recordings + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + Vocal + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + Are you sure you want to delete this item? + + SettingsAdvanced diff --git a/linphone-app/assets/languages/es.ts b/linphone-app/assets/languages/es.ts index 039bd89a1..fea36ac50 100644 --- a/linphone-app/assets/languages/es.ts +++ b/linphone-app/assets/languages/es.ts @@ -1792,6 +1792,11 @@ Haga clic aquí: <a href="%1">%1 </a> 'Check for updates' : Item menu for checking updates Buscar actualizaciones + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1812,6 +1817,11 @@ Haga clic aquí: <a href="%1">%1 </a> 'Check for updates' : Item menu for checking updates Buscar actualizaciones + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2292,6 +2302,24 @@ Haga clic aquí: <a href="%1">%1 </a> + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/fr_FR.ts b/linphone-app/assets/languages/fr_FR.ts index 57547d66e..c800c0525 100644 --- a/linphone-app/assets/languages/fr_FR.ts +++ b/linphone-app/assets/languages/fr_FR.ts @@ -1792,6 +1792,11 @@ Cliquez ici : <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Vérifier les mises à jour + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1812,6 +1817,11 @@ Cliquez ici : <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Vérifier les mises à jour + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2292,6 +2302,24 @@ Cliquez ici : <a href="%1">%1</a> Auto + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/hu.ts b/linphone-app/assets/languages/hu.ts index ce392ee15..32e6d1c33 100644 --- a/linphone-app/assets/languages/hu.ts +++ b/linphone-app/assets/languages/hu.ts @@ -1782,6 +1782,11 @@ Kattintson ide: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Frissítések keresése + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1802,6 +1807,11 @@ Kattintson ide: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Frissítések keresése + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2279,6 +2289,24 @@ Kattintson ide: <a href="%1">%1</a> Önműködő + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/it.ts b/linphone-app/assets/languages/it.ts index c4d768163..5522db170 100644 --- a/linphone-app/assets/languages/it.ts +++ b/linphone-app/assets/languages/it.ts @@ -1792,6 +1792,11 @@ Clicca: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Controlla aggiornamenti + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1812,6 +1817,11 @@ Clicca: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Controlla aggiornamenti + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2292,6 +2302,24 @@ Clicca: <a href="%1">%1</a> Automatico + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/ja.ts b/linphone-app/assets/languages/ja.ts index 557a37a20..952b857d5 100644 --- a/linphone-app/assets/languages/ja.ts +++ b/linphone-app/assets/languages/ja.ts @@ -1782,6 +1782,11 @@ 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1802,6 +1807,11 @@ 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2279,6 +2289,24 @@ + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/lt.ts b/linphone-app/assets/languages/lt.ts index 29cb489f1..ade294354 100644 --- a/linphone-app/assets/languages/lt.ts +++ b/linphone-app/assets/languages/lt.ts @@ -1802,6 +1802,11 @@ Spustelėkite čia: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1822,6 +1827,11 @@ Spustelėkite čia: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2305,6 +2315,24 @@ Spustelėkite čia: <a href="%1">%1</a> + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/pt_BR.ts b/linphone-app/assets/languages/pt_BR.ts index aaced721e..59c7cc682 100644 --- a/linphone-app/assets/languages/pt_BR.ts +++ b/linphone-app/assets/languages/pt_BR.ts @@ -1792,6 +1792,11 @@ Clique aqui: <a href="%1">%1 </a> 'Check for updates' : Item menu for checking updates Verifique se há atualizações + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1812,6 +1817,11 @@ Clique aqui: <a href="%1">%1 </a> 'Check for updates' : Item menu for checking updates Verifique se há atualizações + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2292,6 +2302,24 @@ Clique aqui: <a href="%1">%1 </a> Auto + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/ru.ts b/linphone-app/assets/languages/ru.ts index 770bd2d14..c5a1fc4f9 100644 --- a/linphone-app/assets/languages/ru.ts +++ b/linphone-app/assets/languages/ru.ts @@ -1802,6 +1802,11 @@ 'Check for updates' : Item menu for checking updates Проверить обновления + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1822,6 +1827,11 @@ 'Check for updates' : Item menu for checking updates Проверить обновления + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2305,6 +2315,24 @@ Авто + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/sv.ts b/linphone-app/assets/languages/sv.ts index da6160940..29946ef18 100644 --- a/linphone-app/assets/languages/sv.ts +++ b/linphone-app/assets/languages/sv.ts @@ -1792,6 +1792,11 @@ Klicka här: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1812,6 +1817,11 @@ Klicka här: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2292,6 +2302,24 @@ Klicka här: <a href="%1">%1</a> + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/tr.ts b/linphone-app/assets/languages/tr.ts index 9b6612885..ca4a4fbea 100644 --- a/linphone-app/assets/languages/tr.ts +++ b/linphone-app/assets/languages/tr.ts @@ -1782,6 +1782,11 @@ Buraya tıklayın: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Güncellemeleri denetle + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1802,6 +1807,11 @@ Buraya tıklayın: <a href="%1">%1</a> 'Check for updates' : Item menu for checking updates Güncellemeleri denetle + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2279,6 +2289,24 @@ Buraya tıklayın: <a href="%1">%1</a> Kendiliğinden + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/uk.ts b/linphone-app/assets/languages/uk.ts index 5f7d0b467..bd25ac939 100644 --- a/linphone-app/assets/languages/uk.ts +++ b/linphone-app/assets/languages/uk.ts @@ -1802,6 +1802,11 @@ 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1822,6 +1827,11 @@ 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2305,6 +2315,24 @@ + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/assets/languages/zh_CN.ts b/linphone-app/assets/languages/zh_CN.ts index f715c304e..ca3b009d1 100644 --- a/linphone-app/assets/languages/zh_CN.ts +++ b/linphone-app/assets/languages/zh_CN.ts @@ -1782,6 +1782,11 @@ 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + MainWindowTopMenuBar @@ -1802,6 +1807,11 @@ 'Check for updates' : Item menu for checking updates + + recordings + 'Recordings' : Label for the recordings menu. + + ManageAccounts @@ -2279,6 +2289,24 @@ 自动 + + Recordings + + titleNoRecordings + 'No recordings' : Title of an empty list of records. + + + + recordingsVocalLabel + 'Vocal' : Label for recording type that is a vocal message. + + + + recordingsDelete + 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + + + SettingsAdvanced diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc index efac25df6..6d8eb355d 100644 --- a/linphone-app/resources.qrc +++ b/linphone-app/resources.qrc @@ -135,6 +135,7 @@ assets/images/play_custom.svg assets/images/recording_sign.svg assets/images/record_custom.svg + assets/images/recordings_custom.svg assets/images/remove_participant_custom.svg assets/images/screen_sharing_custom.svg assets/images/screenshot_custom.svg @@ -481,6 +482,7 @@ ui/views/App/Main/MainWindowMenuBar.qml ui/views/App/Main/MainWindow.qml ui/views/App/Main/MainWindowTopMenuBar.qml + ui/views/App/Main/Recordings.qml ui/views/App/Settings/Dialogs/SettingsLdapEdit.qml ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.js ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml @@ -533,6 +535,7 @@ ui/views/App/Styles/Main/InviteFriendsStyle.qml ui/views/App/Styles/Main/HistoryViewStyle.qml ui/views/App/Styles/Main/MainWindowStyle.qml + ui/views/App/Styles/Main/RecordingsStyle.qml ui/views/App/Styles/qmldir ui/views/App/Styles/Settings/Dialogs/SettingsSipAccountsEditStyle.qml ui/views/App/Styles/Settings/Dialogs/SettingsVideoPreviewStyle.qml diff --git a/linphone-app/src/app/App.cpp b/linphone-app/src/app/App.cpp index 5684a9931..861ff4562 100644 --- a/linphone-app/src/app/App.cpp +++ b/linphone-app/src/app/App.cpp @@ -676,6 +676,7 @@ void App::registerTypes () { registerType("HistoryProxyModel"); registerType("LdapProxyModel"); registerType("ParticipantImdnStateProxyModel"); + registerType("RecordingProxyModel"); registerType("SipAddressesProxyModel"); registerType("SearchSipAddressesModel"); registerType("SearchSipAddressesProxyModel"); @@ -711,6 +712,7 @@ void App::registerTypes () { registerUncreatableType("ContactsImporterModel"); registerUncreatableType("ContentModel"); registerUncreatableType("ContentListModel"); + registerUncreatableType("FileMediaModel"); registerUncreatableType("HistoryModel"); registerUncreatableType("LdapModel"); registerUncreatableType("RecorderModel"); diff --git a/linphone-app/src/app/proxyModel/ProxyListModel.hpp b/linphone-app/src/app/proxyModel/ProxyListModel.hpp index 704daf38d..22ba8a5f0 100644 --- a/linphone-app/src/app/proxyModel/ProxyListModel.hpp +++ b/linphone-app/src/app/proxyModel/ProxyListModel.hpp @@ -86,16 +86,18 @@ public: virtual bool remove(QObject *itemToRemove) override{ bool removed = false; - qInfo() << QStringLiteral("Removing ") << itemToRemove->metaObject()->className() << QStringLiteral(" : ") << itemToRemove; - int index = 0; - for(auto item : mList) - if( item == itemToRemove) { - removed = removeRow(index); - break; - }else - ++index; - if( !removed) - qWarning() << QStringLiteral("Unable to remove ") << itemToRemove->metaObject()->className() << QStringLiteral(" : ") << itemToRemove; + if(itemToRemove){ + qInfo() << QStringLiteral("Removing ") << itemToRemove->metaObject()->className() << QStringLiteral(" : ") << itemToRemove; + int index = 0; + for(auto item : mList) + if( item == itemToRemove) { + removed = removeRow(index); + break; + }else + ++index; + if( !removed) + qWarning() << QStringLiteral("Unable to remove ") << itemToRemove->metaObject()->className() << QStringLiteral(" : ") << itemToRemove; + } return removed; } virtual bool remove(QSharedPointer itemToRemove){ diff --git a/linphone-app/src/components/Components.hpp b/linphone-app/src/components/Components.hpp index 52268068a..4545387a1 100644 --- a/linphone-app/src/components/Components.hpp +++ b/linphone-app/src/components/Components.hpp @@ -54,6 +54,7 @@ #include "core/CoreManager.hpp" #include "file/FileDownloader.hpp" #include "file/FileExtractor.hpp" +#include "file/FileMediaModel.hpp" #include "history/HistoryProxyModel.hpp" #include "ldap/LdapModel.hpp" #include "ldap/LdapListModel.hpp" @@ -71,6 +72,8 @@ #include "presence/OwnPresenceModel.hpp" #include "recorder/RecorderModel.hpp" #include "recorder/RecorderManager.hpp" +#include "recorder/RecordingListModel.hpp" +#include "recorder/RecordingProxyModel.hpp" #include "settings/AccountSettingsModel.hpp" #include "settings/SettingsModel.hpp" #include "search/SearchResultModel.hpp" diff --git a/linphone-app/src/components/call/CallModel.cpp b/linphone-app/src/components/call/CallModel.cpp index a62957cdc..dd8952ea1 100644 --- a/linphone-app/src/components/call/CallModel.cpp +++ b/linphone-app/src/components/call/CallModel.cpp @@ -1316,7 +1316,7 @@ QString CallModel::generateSavedFilename () const { QString CallModel::generateSavedFilename (const QString &from, const QString &to) { auto escape = [](const QString &str) { - constexpr char ReservedCharacters[] = "[<|>|:|\"|/|\\\\|\\?|\\*|\\+|\\|]+"; + constexpr char ReservedCharacters[] = "[<|>|:|\"|/|\\\\|\\?|\\*|\\+|\\||_|-]+"; static QRegularExpression regexp(ReservedCharacters); return QString(str).replace(regexp, ""); }; @@ -1325,3 +1325,35 @@ QString CallModel::generateSavedFilename (const QString &from, const QString &to .arg(escape(from)) .arg(escape(to)); } + +QStringList CallModel::splitSavedFilename(const QString& filename){ + QStringList fields = filename.split('_'); + if(fields.size() == 4 && fields[0].split('-').size() == 3 && fields[1].split('-').size() == 3){ + return fields; + }else + return QStringList(filename); +} + +QDateTime CallModel::getDateTimeSavedFilename(const QString& filename){ + auto fields = splitSavedFilename(filename); + if(fields.size() > 1) + return QDateTime::fromString(fields[0] + "_" +fields[1], "yyyy-MM-dd_hh-mm-ss"); + else + return QDateTime(); +} + +QString CallModel::getFromSavedFilename(const QString& filename){ + auto fields = splitSavedFilename(filename); + if(fields.size() > 1) + return fields[2]; + else + return ""; +} + +QString CallModel::getToSavedFilename(const QString& filename){ + auto fields = splitSavedFilename(filename); + if(fields.size() > 1) + return fields[3]; + else + return ""; +} diff --git a/linphone-app/src/components/call/CallModel.hpp b/linphone-app/src/components/call/CallModel.hpp index a4bd3e04e..db9cae4c7 100644 --- a/linphone-app/src/components/call/CallModel.hpp +++ b/linphone-app/src/components/call/CallModel.hpp @@ -320,8 +320,12 @@ public: QString generateSavedFilename () const; +// Format : Date_Time_From_To static QString generateSavedFilename (const QString &from, const QString &to); - + static QStringList splitSavedFilename(const QString& filename);// If doesn't match to generateSavedFilename, return filename + static QDateTime getDateTimeSavedFilename(const QString& filename); + static QString getFromSavedFilename(const QString& filename); + static QString getToSavedFilename(const QString& filename); private: void connectTo(CallListener * listener); diff --git a/linphone-app/src/components/camera/Camera.cpp b/linphone-app/src/components/camera/Camera.cpp index e4f15b93d..31ab63864 100644 --- a/linphone-app/src/components/camera/Camera.cpp +++ b/linphone-app/src/components/camera/Camera.cpp @@ -27,6 +27,7 @@ #include "components/core/CoreManager.hpp" #include "components/participant/ParticipantDeviceModel.hpp" #include "components/settings/SettingsModel.hpp" +#include "components/sound-player/SoundPlayer.hpp" #include "Camera.hpp" #include "CameraDummy.hpp" @@ -104,6 +105,9 @@ void Camera::resetWindowId() const{ oldRenderer = (QQuickFramebufferObject::Renderer *)core->getNativeVideoWindowId(); if(oldRenderer) core->setNativeVideoWindowId(NULL); + }else if(mWindowIdLocation == Player){ + if(mLinphonePlayer && mLinphonePlayer->getLinphonePlayer()) + mLinphonePlayer->getLinphonePlayer()->setWindowId(nullptr); } qDebug() << "[Camera] Removed " << oldRenderer << " at " << mWindowIdLocation << " for " << this; mIsWindowIdSet = false; @@ -148,6 +152,9 @@ void Camera::updateWindowIdLocation(){ setWindowIdLocation(WindowIdLocation::Device); useDefaultWindow = false; } + }else if( mLinphonePlayer){ + setWindowIdLocation(WindowIdLocation::Player); + useDefaultWindow = false; } if(useDefaultWindow){ setWindowIdLocation(WindowIdLocation::Core); @@ -163,6 +170,10 @@ void Camera::removeCallModel(){ mCallModel = nullptr; } +void Camera::removeLinphonePlayer(){ + mLinphonePlayer = nullptr; +} + QQuickFramebufferObject::Renderer *Camera::createRenderer () const { QQuickFramebufferObject::Renderer * renderer = NULL; if(mWindowIdLocation == CorePreview){ @@ -194,6 +205,14 @@ QQuickFramebufferObject::Renderer *Camera::createRenderer () const { renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->createNativeVideoWindowId(); if(renderer) CoreManager::getInstance()->getCore()->setNativeVideoWindowId(renderer); + }else if( mWindowIdLocation == Player){ + auto player = mLinphonePlayer->getLinphonePlayer(); + if(player){ + qDebug() << "[Camera] Setting Camera to Player"; + renderer = (QQuickFramebufferObject::Renderer *) player->createWindowId(); + if(renderer) + player->setWindowId(renderer); + } } if( !renderer){ QTimer::singleShot(1, this, &Camera::isNotReady);// Workaround for const createRenderer @@ -227,6 +246,10 @@ ParticipantDeviceModel * Camera::getParticipantDeviceModel() const{ return mParticipantDeviceModel; } +SoundPlayer * Camera::getLinphonePlayer() const{ + return mLinphonePlayer; +} + void Camera::setCallModel (CallModel *callModel) { if (mCallModel != callModel) { if( mCallModel){ @@ -275,6 +298,17 @@ void Camera::setParticipantDeviceModel(ParticipantDeviceModel * participantDevic emit participantDeviceModelChanged(mParticipantDeviceModel); } } +void Camera::setLinphonePlayer(SoundPlayer *player){ + if (mLinphonePlayer!= player) { + if( mLinphonePlayer) + disconnect(mLinphonePlayer, &QObject::destroyed, this, &Camera::removeLinphonePlayer); + mLinphonePlayer = player; + connect(mLinphonePlayer, &QObject::destroyed, this, &Camera::removeLinphonePlayer); + updateWindowIdLocation(); + update(); + emit linphonePlayerChanged(mLinphonePlayer); + } +} void Camera::isReady(){ setIsReady(true); diff --git a/linphone-app/src/components/camera/Camera.hpp b/linphone-app/src/components/camera/Camera.hpp index afb78e4d7..c544d05f4 100644 --- a/linphone-app/src/components/camera/Camera.hpp +++ b/linphone-app/src/components/camera/Camera.hpp @@ -27,6 +27,8 @@ #include #include +#include "components/sound-player/SoundPlayer.hpp" + // ============================================================================= namespace linphone { @@ -35,6 +37,7 @@ namespace linphone { class CallModel; class ParticipantDeviceModel; + // ----------------------------------------------------------------------------- class Camera : public QQuickFramebufferObject { @@ -44,12 +47,14 @@ class Camera : public QQuickFramebufferObject { Q_PROPERTY(ParticipantDeviceModel * participantDeviceModel READ getParticipantDeviceModel WRITE setParticipantDeviceModel NOTIFY participantDeviceModelChanged) Q_PROPERTY(bool isPreview READ getIsPreview WRITE setIsPreview NOTIFY isPreviewChanged); Q_PROPERTY(bool isReady READ getIsReady WRITE setIsReady NOTIFY isReadyChanged); + Q_PROPERTY(SoundPlayer * linphonePlayer READ getLinphonePlayer WRITE setLinphonePlayer NOTIFY linphonePlayerChanged) typedef enum{ None = -1, CorePreview = 0, Call, Device, + Player, Core }WindowIdLocation; @@ -77,24 +82,28 @@ signals: void participantDeviceModelChanged(ParticipantDeviceModel *participantDeviceModel); void requestNewRenderer(); void videoDefinitionChanged(); + void linphonePlayerChanged(SoundPlayer * linphonePlayer); private: CallModel *getCallModel () const; bool getIsPreview () const; bool getIsReady () const; ParticipantDeviceModel * getParticipantDeviceModel() const; + SoundPlayer * getLinphonePlayer() const; void setCallModel (CallModel *callModel); void setIsPreview (bool status); void setIsReady(bool status); void setParticipantDeviceModel(ParticipantDeviceModel * participantDeviceModel); - void setWindowIdLocation(const WindowIdLocation& location); + void setLinphonePlayer(SoundPlayer *player); + void setWindowIdLocation(const WindowIdLocation& location); void activatePreview(); void deactivatePreview(); void updateWindowIdLocation(); void removeParticipantDeviceModel(); void removeCallModel(); + void removeLinphonePlayer(); QVariantMap mLastVideoDefinition; QTimer mLastVideoDefinitionChecker; @@ -103,6 +112,7 @@ private: bool mIsReady = false; CallModel *mCallModel = nullptr; ParticipantDeviceModel *mParticipantDeviceModel = nullptr; + SoundPlayer * mLinphonePlayer = nullptr; WindowIdLocation mWindowIdLocation = None; mutable bool mIsWindowIdSet = false; diff --git a/linphone-app/src/components/file/FileMediaModel.cpp b/linphone-app/src/components/file/FileMediaModel.cpp new file mode 100644 index 000000000..e36b9ab2a --- /dev/null +++ b/linphone-app/src/components/file/FileMediaModel.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 . + */ + +#include "FileMediaModel.hpp" + +#include + +#include "app/App.hpp" + +#include "components/call/CallModel.hpp" +#include "components/recorder/RecorderModel.hpp" +#include "components/sound-player/SoundPlayer.hpp" + + +// ============================================================================= + +FileMediaModel::FileMediaModel (const QString& path, QObject * parent) :mFileInfo(path), QObject(parent) { + if(path.isEmpty()) return; + init(); +} + +FileMediaModel::FileMediaModel (const QFileInfo& fileInfo, QObject * parent) :mFileInfo(fileInfo), QObject(parent) { + init(); +} + +FileMediaModel::~FileMediaModel(){ +} + +QSharedPointer FileMediaModel::create(const QString& path){ + return FileMediaModel::create(QFileInfo(path)); +} + +QSharedPointer FileMediaModel::create(const QFileInfo& fileInfo){ + auto model = QSharedPointer::create(fileInfo); + return model; +} + +void FileMediaModel::init(){ + QString baseName = getBaseName(); + SoundPlayer soundPlayer; + soundPlayer.setSource(mFileInfo.absoluteFilePath()); + if(soundPlayer.open()){ + mDuration = soundPlayer.getDuration(); + if(CallModel::splitSavedFilename(baseName).size() > 1) + mType = IS_CALL_RECORD; + else if( RecorderModel::splitSavedFilename(baseName).size() > 1) + mType = IS_VOICE_RECORD; + else + mType = IS_PLAYABLE; + }else if(CallModel::splitSavedFilename(baseName).size() > 1) + mType = IS_SNAPSHOT; + else + mType = IS_UNKNOWN; +} +// ----------------------------------------------------------------------------- + +QString FileMediaModel::getBaseName() const{ + return mFileInfo.baseName(); +} + +QString FileMediaModel::getFilePath() const{ + return mFileInfo.absoluteFilePath(); +} + +int FileMediaModel::getDuration() const{ + return mDuration; +} + +FileMediaModel::FILE_TYPE FileMediaModel::getType() const{ + return mType; +} + +QString FileMediaModel::getFrom()const{ + QString baseName = getBaseName(); + switch(mType){ + case IS_CALL_RECORD: case IS_SNAPSHOT: + return CallModel::getFromSavedFilename(baseName); + break; + case IS_VOICE_RECORD: + return ""; + break; + default:{ + return ""; + } + } +} + +QString FileMediaModel::getTo()const{ + QString baseName = getBaseName(); + switch(mType){ + case IS_CALL_RECORD: case IS_SNAPSHOT: + return CallModel::getToSavedFilename(baseName); + break; + case IS_VOICE_RECORD: + return ""; + break; + default:{ + return ""; + } + } +} + +QDateTime FileMediaModel::getCreationDateTime() const{ + QString baseName; + switch(mType){ + case IS_CALL_RECORD: case IS_SNAPSHOT: + baseName = getBaseName(); + return CallModel::getDateTimeSavedFilename(baseName); + break; + case IS_VOICE_RECORD: + baseName = getBaseName(); + return RecorderModel::getDateTimeSavedFilename(baseName); + break; + default:{ + QDateTime creationDate = mFileInfo.birthTime(); + return creationDate.isValid() ? creationDate : mFileInfo.lastModified(); + } + } +} + +QStringList FileMediaModel::getParsedBaseName() const{ + QString baseName = getBaseName(); + switch(mType){ + case IS_CALL_RECORD: case IS_SNAPSHOT: + return CallModel::splitSavedFilename(baseName); + break; + case IS_VOICE_RECORD: + return RecorderModel::splitSavedFilename(baseName); + break; + default:{ + return QStringList(baseName); + } + } +} diff --git a/linphone-app/src/components/file/FileMediaModel.hpp b/linphone-app/src/components/file/FileMediaModel.hpp new file mode 100644 index 000000000..f26362321 --- /dev/null +++ b/linphone-app/src/components/file/FileMediaModel.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 . + */ + +#ifndef FILE_MEDIA_MODEL_H_ +#define FILE_MEDIA_MODEL_H_ + +#include +#include +#include +#include +#include + +// ============================================================================= + +class FileMediaModel : public QObject { + Q_OBJECT + Q_PROPERTY(QString baseName READ getBaseName CONSTANT) + Q_PROPERTY(QStringList parsedBaseName READ getParsedBaseName CONSTANT) + Q_PROPERTY(QString filePath READ getFilePath CONSTANT) + Q_PROPERTY(QDateTime creationDateTime READ getCreationDateTime CONSTANT) + Q_PROPERTY(FILE_TYPE type READ getType CONSTANT) +// App Custom + Q_PROPERTY(int duration READ getDuration CONSTANT) + Q_PROPERTY(QString from READ getFrom CONSTANT) + Q_PROPERTY(QString to READ getTo CONSTANT) +public: + enum FILE_TYPE{ + IS_CALL_RECORD, + IS_VOICE_RECORD, + IS_SNAPSHOT, + IS_PLAYABLE,// playable but nor call nor voice + IS_UNKNOWN + }; + Q_ENUM(FILE_TYPE) + FileMediaModel(const QString& path, QObject * parent = nullptr); + FileMediaModel(const QFileInfo& fileInfo, QObject * parent = nullptr); + ~FileMediaModel(); + static QSharedPointer create(const QString& path); + static QSharedPointer create(const QFileInfo& fileInfo); + + void init(); + + QString getBaseName() const; + QString getFilePath() const; + int getDuration() const; + QDateTime getCreationDateTime() const; + QStringList getParsedBaseName() const; + FILE_TYPE getType()const; + QString getFrom()const; + QString getTo()const; + +private: + QFileInfo mFileInfo; + int mDuration = -1; // Set by LinphonePlayer when cration an instance of FileModel + FILE_TYPE mType = IS_UNKNOWN; +}; + +#endif diff --git a/linphone-app/src/components/other/colors/ColorListModel.hpp b/linphone-app/src/components/other/colors/ColorListModel.hpp index e31b99411..5b8539bfc 100644 --- a/linphone-app/src/components/other/colors/ColorListModel.hpp +++ b/linphone-app/src/components/other/colors/ColorListModel.hpp @@ -191,6 +191,17 @@ class ColorListModel : public ProxyListModel { ADD_COLOR("ma_d_b_fg", "white", "[M] Main disabled button : foreground") ADD_COLOR("ma_h_b_fg", "white", "[M] Main hovered button : foreground") ADD_COLOR("ma_p_b_fg", "white", "[M] Main pressed button : foreground") + +// Inverse + ADD_COLOR("ma_n_b_inv_bg", "transparent", "[M] Main normal button : inverse background") + ADD_COLOR("ma_d_b_inv_bg", "transparent", "[M] Main disabled button : inverse background") + ADD_COLOR("ma_h_b_inv_bg", "transparent", "[M] Main hovered button : inverse background") + ADD_COLOR("ma_p_b_inv_bg", "transparent", "[M] Main pressed button : inverse background") + + ADD_COLOR_WITH_LINK("ma_n_b_inv_fg", "", "[M] Main normal button : inverse foreground", "i") + ADD_COLOR_WITH_LINK("ma_d_b_inv_fg", "", "[M] Main disabled button : inverse foreground", "primary_d") + ADD_COLOR_WITH_LINK("ma_h_b_inv_fg", "", "[M] Main hovered button : inverse foreground", "b") + ADD_COLOR_WITH_LINK("ma_p_b_inv_fg", "", "[M] Main pressed button : inverse foreground", "m") //------------------------------------- // Accept Actions : like accepting a call ADD_COLOR_WITH_LINK("a_n_b_bg", "", "[M] Accept normal button : background", "primary_accept") diff --git a/linphone-app/src/components/recorder/RecorderModel.cpp b/linphone-app/src/components/recorder/RecorderModel.cpp index 75dd5a114..955875022 100644 --- a/linphone-app/src/components/recorder/RecorderModel.cpp +++ b/linphone-app/src/components/recorder/RecorderModel.cpp @@ -64,6 +64,23 @@ QString RecorderModel::getFile()const{ return Utils::coreStringToAppString(mRecorder->getFile()); } +QStringList RecorderModel::splitSavedFilename(const QString& filename){ + QStringList fields = filename.split('_'); + if(fields.size() == 3 && fields[0] == "vocal" && fields[1].split('-').size() == 3 + && fields[2].split('-').size() == 4){ + return fields; + }else + return QStringList(filename); +} + +QDateTime RecorderModel::getDateTimeSavedFilename(const QString& filename){ + auto fields = splitSavedFilename(filename); + if(fields.size() > 1) + return QDateTime::fromString(fields[1] + "_" +fields[2], "yyyy-MM-dd_hh-mm-ss-zzz"); + else + return QDateTime();; +} + void RecorderModel::start(){ bool soFarSoGood; QString filename = QStringLiteral("vocal_%1.mkv") diff --git a/linphone-app/src/components/recorder/RecorderModel.hpp b/linphone-app/src/components/recorder/RecorderModel.hpp index db9359a07..77b4550c9 100644 --- a/linphone-app/src/components/recorder/RecorderModel.hpp +++ b/linphone-app/src/components/recorder/RecorderModel.hpp @@ -44,6 +44,9 @@ public: LinphoneEnums::RecorderState getState() const; Q_INVOKABLE QString getFile()const; + static QStringList splitSavedFilename(const QString& filename);// If doesn't match to generateSavedFilename, return filename + static QDateTime getDateTimeSavedFilename(const QString& filename); + Q_INVOKABLE void start(); Q_INVOKABLE void pause(); Q_INVOKABLE void stop(); diff --git a/linphone-app/src/components/recorder/RecordingListModel.cpp b/linphone-app/src/components/recorder/RecordingListModel.cpp new file mode 100644 index 000000000..309dee249 --- /dev/null +++ b/linphone-app/src/components/recorder/RecordingListModel.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 . + */ + +#include "app/App.hpp" +#include "components/core/CoreManager.hpp" +#include "components/file/FileMediaModel.hpp" +#include "components/settings/AccountSettingsModel.hpp" +#include "components/settings/SettingsModel.hpp" +#include "components/sip-addresses/SipAddressesModel.hpp" +#include "utils/Utils.hpp" + +#include "RecordingListModel.hpp" + +#include +#include + + +// ============================================================================= + +RecordingListModel::RecordingListModel (QObject *parent) : ProxyListModel(parent) { + load(); +} + +RecordingListModel::~RecordingListModel(){ + mList.clear(); +} + +// ----------------------------------------------------------------------------- + +void RecordingListModel::load(){ + resetData(); + QString folder = CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder(); + qInfo() << "[Recordings] looking for recordings in " << CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder(); + QDir dir( folder ); + QList> files; + foreach(QFileInfo file, dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot)) { + auto recording = FileMediaModel::create(file); + if(recording) { + App::getInstance()->getEngine()->setObjectOwnership(recording.get(), QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE + files << recording; + } + } + if(files.size() > 0) + add(files); +} + +QHash RecordingListModel::roleNames () const { + QHash roles = ProxyListModel::roleNames(); + roles[Qt::DisplayRole+1] = "$sectionDate"; + return roles; +} + +QVariant RecordingListModel::data (const QModelIndex &index, int role) const{ + int row = index.row(); + if (!index.isValid() || row < 0 || row >= mList.count()) + return QVariant(); + if(role == Qt::DisplayRole +1){ + return QVariant::fromValue(mList[row].objectCast()->getCreationDateTime().date()); + }else + return ProxyListModel::data(index, role); +} diff --git a/linphone-app/src/components/recorder/RecordingListModel.hpp b/linphone-app/src/components/recorder/RecordingListModel.hpp new file mode 100644 index 000000000..d22c9f02a --- /dev/null +++ b/linphone-app/src/components/recorder/RecordingListModel.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 . + */ + +#ifndef RECORDING_LIST_MODEL_H_ +#define RECORDING_LIST_MODEL_H_ + +#include "app/proxyModel/ProxyListModel.hpp" +#include "components/sound-player/SoundPlayer.hpp" + +// ============================================================================= + +class RecordingListModel : public ProxyListModel { + Q_OBJECT +public: + RecordingListModel (QObject *parent = Q_NULLPTR); + virtual ~RecordingListModel(); + + void load(); + + QHash roleNames () const override; + virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + Q_INVOKABLE void remove (SoundPlayer *player); + Q_INVOKABLE SoundPlayer* getSoundPlayer() const; + */ + +}; +#endif diff --git a/linphone-app/src/components/recorder/RecordingProxyModel.cpp b/linphone-app/src/components/recorder/RecordingProxyModel.cpp new file mode 100644 index 000000000..1e829df13 --- /dev/null +++ b/linphone-app/src/components/recorder/RecordingProxyModel.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 . + */ + +#include "RecordingProxyModel.hpp" + +#include "components/core/CoreManager.hpp" +#include "components/settings/AccountSettingsModel.hpp" +#include "components/sip-addresses/SipAddressesModel.hpp" +#include "components/conference/ConferenceModel.hpp" +#include "components/conferenceInfo/ConferenceInfoModel.hpp" +#include "components/file/FileMediaModel.hpp" +#include "components/sound-player/SoundPlayer.hpp" +#include "utils/Utils.hpp" + +#include "RecordingListModel.hpp" + +#include + + +// ============================================================================= + +// ----------------------------------------------------------------------------- + +RecordingProxyModel::RecordingProxyModel (QObject *parent) : SortFilterProxyModel(parent) { + auto list = new RecordingListModel(this); + setSourceModel(list); + sort(0); +} + +// ----------------------------------------------------------------------------- + +void RecordingProxyModel::remove(FileMediaModel * fileModel){ + QFile file(fileModel->getFilePath()); + if(file.remove()) + qobject_cast(sourceModel())->remove(fileModel); +} + +bool RecordingProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { + const FileMediaModel* a = sourceModel()->data(left).value(); + const FileMediaModel* b = sourceModel()->data(right).value(); + + return a->getCreationDateTime() > b->getCreationDateTime(); +} diff --git a/linphone-app/src/components/recorder/RecordingProxyModel.hpp b/linphone-app/src/components/recorder/RecordingProxyModel.hpp new file mode 100644 index 000000000..21715ae65 --- /dev/null +++ b/linphone-app/src/components/recorder/RecordingProxyModel.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * 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 . + */ + +#ifndef RECORDING_PROXY_MODEL_H_ +#define RECORDING_PROXY_MODEL_H_ + +#include "app/proxyModel/SortFilterProxyModel.hpp" +#include + +// ============================================================================= + +class FileMediaModel; + +class RecordingProxyModel : public SortFilterProxyModel { + Q_OBJECT + +public: + RecordingProxyModel ( QObject *parent = Q_NULLPTR); + + //bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; + bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; + Q_INVOKABLE void remove(FileMediaModel * file); + //Q_INVOKABLE int getCount() const; +}; + +#endif diff --git a/linphone-app/src/components/sound-player/SoundPlayer.cpp b/linphone-app/src/components/sound-player/SoundPlayer.cpp index eb8002dbe..0ba8597fc 100644 --- a/linphone-app/src/components/sound-player/SoundPlayer.cpp +++ b/linphone-app/src/components/sound-player/SoundPlayer.cpp @@ -93,26 +93,33 @@ void SoundPlayer::pause () { emit playbackStateChanged(mPlaybackState); } -void SoundPlayer::play () { - if (mPlaybackState == SoundPlayer::PlayingState || mSource == "") - return; +bool SoundPlayer::open(){ + return mInternalPlayer->open(Utils::appStringToCoreString(mSource)) == 0; +} + +bool SoundPlayer::play () { + if (mPlaybackState == SoundPlayer::PlayingState) + return true; + else if(mSource == "") + return false; if ( (mPlaybackState == SoundPlayer::StoppedState || mPlaybackState == SoundPlayer::ErrorState) && - mInternalPlayer->open(Utils::appStringToCoreString(mSource)) + !open() ) { qWarning() << QStringLiteral("Unable to open: `%1`").arg(mSource); - return; + return false; } if (mInternalPlayer->start() ) { setError(QStringLiteral("Unable to play: `%1`").arg(mSource)); - return; + return false; } mForceCloseTimer->start(); mPlaybackState = SoundPlayer::PlayingState; emit playing(); emit playbackStateChanged(mPlaybackState); + return true; } void SoundPlayer::stop () { @@ -131,6 +138,10 @@ int SoundPlayer::getPosition () const { return mInternalPlayer->getCurrentPosition(); } +bool SoundPlayer::hasVideo() const{ + return mInternalPlayer->getIsVideoAvailable(); +} + // ----------------------------------------------------------------------------- void SoundPlayer::buildInternalPlayer () { @@ -229,3 +240,17 @@ void SoundPlayer::setPlaybackState (PlaybackState playbackState) { int SoundPlayer::getDuration () const { return mInternalPlayer->getDuration(); } + +QDateTime SoundPlayer::getCreationDateTime() const{ + QFileInfo fileInfo(mSource); + QDateTime creationDate = fileInfo.birthTime(); + return creationDate.isValid() ? creationDate : fileInfo.lastModified(); +} + +QString SoundPlayer::getBaseName() const{ + return QFileInfo(mSource).baseName(); +} + +std::shared_ptr SoundPlayer::getLinphonePlayer()const{ + return mInternalPlayer; +} \ No newline at end of file diff --git a/linphone-app/src/components/sound-player/SoundPlayer.hpp b/linphone-app/src/components/sound-player/SoundPlayer.hpp index c0293abb3..111033320 100644 --- a/linphone-app/src/components/sound-player/SoundPlayer.hpp +++ b/linphone-app/src/components/sound-player/SoundPlayer.hpp @@ -40,9 +40,11 @@ class SoundPlayer : public QObject { Q_OBJECT Q_PROPERTY(QString source READ getSource WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QString baseName READ getBaseName NOTIFY sourceChanged) Q_PROPERTY(PlaybackState playbackState READ getPlaybackState WRITE setPlaybackState NOTIFY playbackStateChanged) Q_PROPERTY(int duration READ getDuration NOTIFY sourceChanged) Q_PROPERTY(bool isRinger MEMBER mIsRinger) + Q_PROPERTY(QDateTime creationDateTime READ getCreationDateTime NOTIFY sourceChanged) public: enum PlaybackState { @@ -56,13 +58,23 @@ public: SoundPlayer (QObject *parent = Q_NULLPTR); ~SoundPlayer (); + bool open(); Q_INVOKABLE void pause (); - Q_INVOKABLE void play (); + Q_INVOKABLE bool play (); Q_INVOKABLE void stop (); Q_INVOKABLE void seek (int offset); Q_INVOKABLE int getPosition () const; + Q_INVOKABLE bool hasVideo() const;// Call it after playing a video because the detection is not outside this scope. + + int getDuration () const; + QDateTime getCreationDateTime() const; + QString getBaseName() const; + std::shared_ptr getLinphonePlayer()const; + + QString getSource () const; + void setSource (const QString &source); signals: void sourceChanged (const QString &source); @@ -83,13 +95,10 @@ private: void setError (const QString &message); - QString getSource () const; - void setSource (const QString &source); - PlaybackState getPlaybackState () const; void setPlaybackState (PlaybackState playbackState); - int getDuration () const; + QString mSource; PlaybackState mPlaybackState = StoppedState; diff --git a/linphone-app/ui/modules/Linphone/Camera/CameraItem.qml b/linphone-app/ui/modules/Linphone/Camera/CameraItem.qml index 7e0d04d3b..2eca6d422 100644 --- a/linphone-app/ui/modules/Linphone/Camera/CameraItem.qml +++ b/linphone-app/ui/modules/Linphone/Camera/CameraItem.qml @@ -18,6 +18,7 @@ Item { property bool isCameraFromDevice: true property ParticipantDeviceModel currentDevice property CallModel callModel + property SoundPlayer linphonePlayer property bool isPreview: (!callModel && !container.currentDevice) || ( container.currentDevice && container.currentDevice.isMe) property bool isFullscreen: false property bool hideCamera: false @@ -77,6 +78,7 @@ Item { id: camera Camera { participantDeviceModel: container.currentDevice + linphonePlayer: container.linphonePlayer call: container.isCameraFromDevice ? null : container.callModel anchors.fill: parent isPreview: container.isPreview diff --git a/linphone-app/ui/modules/Linphone/Camera/CameraView.qml b/linphone-app/ui/modules/Linphone/Camera/CameraView.qml index 9d1e00139..8f5414d65 100644 --- a/linphone-app/ui/modules/Linphone/Camera/CameraView.qml +++ b/linphone-app/ui/modules/Linphone/Camera/CameraView.qml @@ -15,6 +15,7 @@ Item{ id: mainItem property alias currentDevice: camera.currentDevice property alias callModel: camera.callModel + property alias linphonePlayer : camera.linphonePlayer property alias hideCamera: camera.hideCamera property alias isPaused: camera.isPaused property alias isPreview: camera.isPreview diff --git a/linphone-app/ui/views/App/Main/MainWindow.qml b/linphone-app/ui/views/App/Main/MainWindow.qml index b60cc6661..17f76eda7 100644 --- a/linphone-app/ui/views/App/Main/MainWindow.qml +++ b/linphone-app/ui/views/App/Main/MainWindow.qml @@ -75,7 +75,6 @@ ApplicationWindow { readonly property alias conferencesEntry: conferencesEntry readonly property alias contentLoader: contentLoader - //readonly property alias conferencesEntry: conferencesEntry readonly property alias menu: menu readonly property alias timeline: timeline @@ -266,6 +265,10 @@ ApplicationWindow { onClicked: toggled ? menuBar.close() : menuBar.open()// a bit useless as Menu will depopup on losing focus but this code is kept for giving idea MainWindowMenuBar { id: menuBar + onDisplayRecordings: { + timeline.model.unselectAll() + setView('Recordings') + } } } } @@ -325,6 +328,7 @@ ApplicationWindow { } } + ApplicationMenuEntry { id: conferencesEntry @@ -413,7 +417,12 @@ ApplicationWindow { Loader{ id: customMenuBar active:Qt.platform.os === 'osx' - sourceComponent:MainWindowTopMenuBar{} + sourceComponent:MainWindowTopMenuBar{ + onDisplayRecordings: { + timeline.model.unselectAll() + setView('Recordings') + } + } } Component.onCompleted: if(Qt.platform.os === 'osx') menuBar = customMenuBar // --------------------------------------------------------------------------- diff --git a/linphone-app/ui/views/App/Main/MainWindowMenuBar.qml b/linphone-app/ui/views/App/Main/MainWindowMenuBar.qml index f02d0f489..5eafac0f1 100644 --- a/linphone-app/ui/views/App/Main/MainWindowMenuBar.qml +++ b/linphone-app/ui/views/App/Main/MainWindowMenuBar.qml @@ -18,6 +18,8 @@ Item { menu.close() } + signal displayRecordings() + // --------------------------------------------------------------------------- // Shortcuts. // --------------------------------------------------------------------------- @@ -50,6 +52,11 @@ Item { } } + Shortcut { + id: recordingsShortcut + onActivated: menuParent.displayRecordings() + } + // --------------------------------------------------------------------------- // Menu. // --------------------------------------------------------------------------- @@ -64,6 +71,12 @@ Item { onTriggered: settingsShortcut.onActivated() } + MenuItem{ + //: 'Recordings' : Label for the recordings menu. + text: qsTr('recordings') + onTriggered: recordingsShortcut.onActivated() + } + MenuItem { visible: CoreManager.initialized && SettingsModel.isCheckForUpdateAvailable() //: 'Check for updates' : Item menu for checking updates diff --git a/linphone-app/ui/views/App/Main/MainWindowTopMenuBar.qml b/linphone-app/ui/views/App/Main/MainWindowTopMenuBar.qml index ee9425d5b..a4e8e8476 100644 --- a/linphone-app/ui/views/App/Main/MainWindowTopMenuBar.qml +++ b/linphone-app/ui/views/App/Main/MainWindowTopMenuBar.qml @@ -6,10 +6,12 @@ import Linphone 1.0 // ============================================================================= MenuBar { + id: menuBar function open () { menu.open() } - + signal displayRecordings() + // --------------------------------------------------------------------------- // Menu. // --------------------------------------------------------------------------- @@ -25,6 +27,12 @@ MenuBar { shortcut: StandardKey.Preferences } + MenuItem { + //: 'Recordings' : Label for the recordings menu. + text: qsTr('recordings') + role: MenuItem.ApplicationSpecificRole + onTriggered: menuBar.displayRecordings() + } MenuItem { visible: CoreManager.initialized && SettingsModel.isCheckForUpdateAvailable() diff --git a/linphone-app/ui/views/App/Main/Recordings.qml b/linphone-app/ui/views/App/Main/Recordings.qml new file mode 100644 index 000000000..714dbb31f --- /dev/null +++ b/linphone-app/ui/views/App/Main/Recordings.qml @@ -0,0 +1,291 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.7 +import QtQuick.Layouts 1.10 + +import App 1.0 +import Common 1.0 +import Linphone 1.0 +import Utils 1.0 +import UtilsCpp 1.0 +import ColorsList 1.0 +import Units 1.0 + + +import App.Styles 1.0 + +// ============================================================================= + +Item { + Item{ + id: mainItem + anchors.fill: parent + anchors.topMargin : 15 + anchors.bottomMargin : 15 + anchors.leftMargin : 15 + anchors.rightMargin : 0 + Text { + id: noRec + anchors.centerIn: parent + //: 'No recordings' : Title of an empty list of records. + text: qsTr('titleNoRecordings') + visible : recordingsProxyModel.count === 0 + color: RecordingsStyle.title.color + font.pointSize: RecordingsStyle.title.pointSize + } + Component { + id: sectionHeading + Form { + anchors.rightMargin : 10 + required property string section + title: section + width: parent.width + height: 30 + } + } + ScrollableListView { + anchors.fill: parent + id: recordingsList + spacing: 0 + model: RecordingProxyModel { + id: recordingsProxyModel + } + section.property: '$sectionDate' + section.criteria: ViewSection.FullString + section.delegate: sectionHeading + delegate: Loader{ + id: lineLoader + property bool isMedia: $modelData && ($modelData.type != FileMediaModel.IS_UNKNOWN && $modelData.type != FileMediaModel.IS_SNAPSHOT) // Test only extension because file can be encrypted. + property string title: ($modelData.type == FileMediaModel.IS_CALL_RECORD || $modelData.type == FileMediaModel.IS_SNAPSHOT + ? $modelData.from + ' => ' +$modelData.to + : $modelData.type == FileMediaModel.IS_VOICE_RECORD + //: 'Vocal' : Label for recording type that is a vocal message. + ? qsTr('recordingsVocalLabel') + : $modelData.baseName) + + ' - ' +UtilsCpp.toTimeString($modelData.creationDateTime) + sourceComponent: isMedia ? mediaComponent : fileComponent +//-------------------------------------------------------------------------- +// MEDIA +//-------------------------------------------------------------------------- + Component{ + id: mediaComponent + RowLayout { + id: lineItem + width: recordingsList.width + property bool isPlaying : vocalPlayer.item && vocalPlayer.item.playbackState === SoundPlayer.PlayingState + onIsPlayingChanged: { + if (isPlaying) { + if(mediaProgressBar.value >= 100) + mediaProgressBar.value = 0 + timer.start() + } else { + timer.stop() + } + } + Loader{ + id: vocalPlayer + active: false + sourceComponent: SoundPlayer { + id: player + source: $modelData.filePath + onStopped: { + mediaProgressBar.value = 101 + videoView.linphonePlayer = null + } + Component.onCompleted: { + mediaProgressBar.value = 0 + play() + videoView.linphonePlayer = null + if( player.hasVideo()) + videoView.linphonePlayer = player + } + } + } + ActionButton { + Layout.alignment: Qt.AlignRight + isCustom: true + colorSet: lineItem.isPlaying ? RecordingsStyle.buttons.pause : RecordingsStyle.buttons.play + onClicked: { + if(!vocalPlayer.active) + vocalPlayer.active = true + else if(lineItem.isPlaying){// Pause the play + vocalPlayer.item.pause() + }else{// Play the audio + vocalPlayer.item.play() + videoView.linphonePlayer = null + if(vocalPlayer.item.hasVideo()) + videoView.linphonePlayer = vocalPlayer.item + } + } + } + ColumnLayout { + Layout.rightMargin : 15 + Layout.leftMargin : 30 + Layout.fillWidth: true + spacing:0 + RowLayout { + Layout.fillWidth: true + Layout.topMargin: 10 + Text { + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + text: lineLoader.title + horizontalAlignment: Text.AlignLeft + font.pointSize: RecordingsStyle.filename.pointSize + color: RecordingsStyle.filename.color + } + Text { + id: durationText + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight + text: (vocalPlayer.item ? Utils.formatElapsedTime(vocalPlayer.item.getPosition()/1000) + "/" : '') + +Utils.formatElapsedTime($modelData.duration/1000) + horizontalAlignment: Text.AlignRight + font.pointSize: RecordingsStyle.filename.pointSize + color: RecordingsStyle.filename.color + } + } + Slider { + id: mediaProgressBar + Layout.fillWidth: true + Layout.leftMargin: 20 + enabled: true + to: 101 + value: vocalPlayer.item ? 0.01 * progressDuration / 5 : 0 + Timer{ + id: timer + repeat: true + onTriggered: { + if( vocalPlayer.item){ + mediaProgressBar.value = 100 * ( vocalPlayer.item.getPosition() / vocalPlayer.item.duration) + durationText.text = Utils.formatElapsedTime(vocalPlayer.item.getPosition()/1000) + "/" + Utils.formatElapsedTime(vocalPlayer.item.duration/1000) + } + } + interval: 5 + } + onValueChanged:{ + if(value > 100){ + timer.stop() + durationText.text = Utils.formatElapsedTime(0) + "/" + Utils.formatElapsedTime(vocalPlayer.item.duration/1000) + if(vocalPlayer.item) + vocalPlayer.item.stop() + value = 0 + } + } + onMoved: if(vocalPlayer.item){ + vocalPlayer.item.seek(vocalPlayer.item.duration*value / 100) + value = 100 * (vocalPlayer.item.getPosition() / vocalPlayer.item.duration) + } + } + } + + ActionButton { + Layout.rightMargin : 30 + Layout.leftMargin : 15 + isCustom: true + backgroundRadius: width/2 + colorSet: RecordingsStyle.buttons.remove + onClicked: { + window.detachVirtualWindow() + window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { + //: 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + descriptionText: qsTr('recordingsDelete'), + }, function (status) { + if (status) { + recordingsProxyModel.remove($modelData) + } + }) + + } + } + } + } +//-------------------------------------------------------------------------- +// FILE +//-------------------------------------------------------------------------- + Component{ + id: fileComponent + RowLayout{ + width: recordingsList.width + Item{ + height: RecordingsStyle.buttons.size + width: height + ActionButton{ + anchors.centerIn: parent + isCustom: true + backgroundRadius: width/2 + colorSet: $modelData.type == FileMediaModel.IS_SNAPSHOT ? RecordingsStyle.buttons.openImage : RecordingsStyle.buttons.openFile + onClicked: { + Qt.openUrlExternally(Utils.getUriFromSystemPath($modelData.filePath)) + } + } + } + Text{ + Layout.fillWidth: true + Layout.leftMargin : 30 + text: lineLoader.title + font.pointSize: RecordingsStyle.filename.pointSize + color: RecordingsStyle.filename.color + } + ActionButton { + Layout.rightMargin : 30 + Layout.leftMargin : 15 + isCustom: true + backgroundRadius: width/2 + colorSet: RecordingsStyle.buttons.remove + onClicked: { + window.detachVirtualWindow() + window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { + //: 'Are you sure you want to delete this item?' : Confirmation message for removing a record. + descriptionText: qsTr('recordingsDelete'), + }, function (status) { + if (status) { + recordingsProxyModel.remove($modelData) + } + }) + + } + } + } + } + }// Loader + } + Item{ + id: videoViewItem + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + height: 200 + width: height * 16/9 + visible: videoView.active + Loader{ + id: videoView + property SoundPlayer linphonePlayer + anchors.fill: parent + active: linphonePlayer + sourceComponent: Component{ + CameraView{ + isPreview: false + linphonePlayer: videoView.linphonePlayer + } + } + } + + MovableMouseArea{ + id: dragger + anchors.fill: parent + function resetPosition(){ + videoViewItem.anchors.bottom = mainItem.bottom + videoViewItem.anchors.horizontalCenter = mainItem.horizontalCenter + } + onVisibleChanged: if(!visible){ + resetPosition() + } + drag.target: videoViewItem + onDraggingChanged: if(dragging){ + videoViewItem.anchors.bottom = undefined + videoViewItem.anchors.horizontalCenter = undefined + } + onRequestResetPosition: resetPosition() + } + } + } +} diff --git a/linphone-app/ui/views/App/Styles/Main/MainWindowStyle.qml b/linphone-app/ui/views/App/Styles/Main/MainWindowStyle.qml index 655779f01..ef29f8084 100644 --- a/linphone-app/ui/views/App/Styles/Main/MainWindowStyle.qml +++ b/linphone-app/ui/views/App/Styles/Main/MainWindowStyle.qml @@ -50,6 +50,12 @@ QtObject { property color color: ColorsList.add(sectionName+'_me_conferences', 'me_n_b_inv_fg').color property color selectedColor: ColorsList.add(sectionName+'_me_conferences_c', 'me_p_b_inv_fg').color } + property QtObject recordings: QtObject { + property string icon: 'recordings_custom' + property int iconSize: 50 + property color color: ColorsList.add(sectionName+'_me_recordings', 'me_n_b_inv_fg').color + property color selectedColor: ColorsList.add(sectionName+'_me_recordings_c', 'me_p_b_inv_fg').color + } /* property string conferencesIcon: 'conference' property color conferencesColor: ColorsList.add(sectionName+'_me_confs', 'me_n_b_inv_fg').color diff --git a/linphone-app/ui/views/App/Styles/Main/RecordingsStyle.qml b/linphone-app/ui/views/App/Styles/Main/RecordingsStyle.qml new file mode 100644 index 000000000..1e17dbb90 --- /dev/null +++ b/linphone-app/ui/views/App/Styles/Main/RecordingsStyle.qml @@ -0,0 +1,80 @@ +pragma Singleton +import QtQuick 2.7 + +import Units 1.0 +import ColorsList 1.0 + +// ============================================================================= + +QtObject { + property string sectionName : 'Recordings' + + property QtObject title: QtObject { + property color color: ColorsList.add(sectionName+'_title', 'j').color + property int pointSize: Units.dp * 12 + } + + property QtObject filename: QtObject { + property color color: ColorsList.add(sectionName+'_filename', 'j').color + property int pointSize: Units.dp * 10 + } + + property QtObject buttons: QtObject { + property int size: 40 + property QtObject play: QtObject { + property int iconSize: buttons.size + property string name : 'play' + property string icon : 'chat_audio_play_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'ma_n_b_inv_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'ma_h_b_inv_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'ma_p_b_inv_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'ma_n_b_inv_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'ma_h_b_inv_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'ma_p_b_inv_fg').color + } + property QtObject pause: QtObject { + property int iconSize: buttons.size + property string name : 'pause' + property string icon : 'chat_audio_pause_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'ma_n_b_inv_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'ma_h_b_inv_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'ma_p_b_inv_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'ma_n_b_inv_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'ma_h_b_inv_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'ma_p_b_inv_fg').color + } + property QtObject remove: QtObject { + property int iconSize: buttons.size + property string name : 'delete' + property string icon : 'delete_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color + } + property QtObject openImage: QtObject { + property int iconSize: buttons.size/2 + property string name : 'openImage' + property string icon : 'file_unknown_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'ma_n_b_inv_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'ma_h_b_inv_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'ma_p_b_inv_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'ma_n_b_inv_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'ma_h_b_inv_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'ma_p_b_inv_fg').color + } + property QtObject openFile: QtObject { + property int iconSize: buttons.size/2 + property string name : 'openFile' + property string icon : 'file_extension_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'ma_n_b_inv_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'ma_h_b_inv_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'ma_p_b_inv_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'ma_n_b_inv_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'ma_h_b_inv_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'ma_p_b_inv_fg').color + } + } +} diff --git a/linphone-app/ui/views/App/Styles/qmldir b/linphone-app/ui/views/App/Styles/qmldir index add72cbbe..d39c67c57 100644 --- a/linphone-app/ui/views/App/Styles/qmldir +++ b/linphone-app/ui/views/App/Styles/qmldir @@ -40,6 +40,7 @@ singleton HomeStyle 1.0 Main/HomeStyle.qml singleton HistoryViewStyle 1.0 Main/HistoryViewStyle.qml singleton InviteFriendsStyle 1.0 Main/InviteFriendsStyle.qml singleton MainWindowStyle 1.0 Main/MainWindowStyle.qml +singleton RecordingsStyle 1.0 Main/RecordingsStyle.qml singleton AboutStyle 1.0 Main/Dialogs/AboutStyle.qml singleton AuthenticationRequestStyle 1.0 Main/Dialogs/AuthenticationRequestStyle.qml diff --git a/linphone-sdk b/linphone-sdk index e57d22dca..d6c7561ff 160000 --- a/linphone-sdk +++ b/linphone-sdk @@ -1 +1 @@ -Subproject commit e57d22dca56af8ecd9b0ee5b8e072dbdd65d2266 +Subproject commit d6c7561ffaecb43bce24e5f34c1a8e023fde6503