mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 03:18:07 +00:00
Display media captures files (recordings)
This commit is contained in:
parent
62c72bd0e5
commit
5dc002197d
47 changed files with 1558 additions and 28 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
85
linphone-app/assets/images/recordings_custom.svg
Normal file
85
linphone-app/assets/images/recordings_custom.svg
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 80 80"
|
||||
version="1.1"
|
||||
id="svg19"
|
||||
sodipodi:docname="menu_recordings.svg"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs23" />
|
||||
<sodipodi:namedview
|
||||
id="namedview21"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="8.9569892"
|
||||
inkscape:cx="33.716687"
|
||||
inkscape:cy="46.444178"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1163"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="500"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg19" />
|
||||
<title
|
||||
id="title2">menu_recordings@3x</title>
|
||||
<g
|
||||
id="menu_recordings"
|
||||
stroke="none"
|
||||
stroke-width="1"
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="matrix(0.43010753,0,0,0.43010753,22.5,20)">
|
||||
<path
|
||||
d="m 44,49.408775 c 0,1.380712 -1.119288,2.5 -2.5,2.5 -1.380712,0 -2.5,-1.119288 -2.5,-2.5 V 15.858977 C 39,9.8671957 34.088652,5 28.019919,5 21.953212,5 17.041545,9.8677246 17.041545,15.858977 v 33.549798 c 0,5.988072 4.911888,10.854733 10.978374,10.854733 1.380712,0 2.5,1.119288 2.5,2.5 0,1.380712 -1.119288,2.5 -2.5,2.5 -8.81761,0 -15.978374,-7.094831 -15.978374,-15.854733 V 15.858977 C 12.041545,7.0961924 19.201797,0 28.019919,0 36.840019,0 44,7.0956142 44,15.858977 Z"
|
||||
fill="#444445"
|
||||
id="path4" />
|
||||
<polygon
|
||||
fill="#444445"
|
||||
points="30.51992,72.988783 25.51992,72.988783 25.51992,88.739626 30.51992,88.739626 "
|
||||
id="polygon6" />
|
||||
<path
|
||||
d="m 30.519919,72.988783 v 15.750843 c 0,3.333334 -5,3.333334 -5,0 V 72.988783 c 0,-3.333334 5,-3.333334 5,0 z"
|
||||
fill="#444445"
|
||||
id="path8" />
|
||||
<polygon
|
||||
fill="#444445"
|
||||
points="21.078967,88 21.078967,93 34.404614,93 34.404614,88 "
|
||||
id="polygon10" />
|
||||
<path
|
||||
d="m 21.078967,88 h 13.325647 c 3.333333,0 3.333333,5 0,5 H 21.078967 c -3.333333,0 -3.333333,-5 0,-5 z"
|
||||
fill="#444445"
|
||||
id="path12" />
|
||||
<path
|
||||
d="m 0,33.123199 c 0,-1.380712 1.1192881,-2.5 2.5,-2.5 1.3807119,0 2.5,1.119288 2.5,2.5 v 14.574432 c 0,12.583093 10.301683,22.791152 23.020773,22.791152 1.380712,0 2.5,1.119288 2.5,2.5 0,1.380712 -1.119288,2.5 -2.5,2.5 C 12.550426,75.488783 0,63.052418 0,47.697631 Z"
|
||||
fill="#444445"
|
||||
id="path14" />
|
||||
<path
|
||||
d="M 44,85.026212 62.729894,73.500726 44,61.974034 Z m -1.189707,-29.655327 26,16.000838 c 1.586319,0.976247 1.586265,3.282118 -9.9e-5,4.258291 l -26,15.999162 C 41.144517,92.654154 39,91.455776 39,89.5 v -32 c 0,-1.955828 2.14462,-3.154199 3.810293,-2.129115 z"
|
||||
fill="#444445"
|
||||
fill-rule="nonzero"
|
||||
id="path16" />
|
||||
</g>
|
||||
<metadata
|
||||
id="metadata840">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title>menu_recordings@3x</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
|
@ -1792,6 +1792,11 @@ Klik her: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished">Tjek for opdateringer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1812,6 +1817,11 @@ Klik her: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished">Tjek for opdateringer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2292,6 +2302,24 @@ Klik her: <a href="%1">%1</a>
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1792,6 +1792,11 @@ Klicken Sie hier: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Auf Aktualisierungen prüfen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1812,6 +1817,11 @@ Klicken Sie hier: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Auf Aktualisierungen prüfen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2292,6 +2302,24 @@ Klicken Sie hier: <a href="%1">%1</a>
|
|||
<translation>Automatisch</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1792,6 +1792,11 @@ Click here: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Check for updates</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation>Recordings</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1812,6 +1817,11 @@ Click here: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Check for updates</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation>Recordings</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2292,6 +2302,24 @@ Click here: <a href="%1">%1</a>
|
|||
<translation>Auto</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation>No recordings</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation>Vocal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation>Are you sure you want to delete this item?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1792,6 +1792,11 @@ Haga clic aquí: <a href="%1">%1 </a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished">Buscar actualizaciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1812,6 +1817,11 @@ Haga clic aquí: <a href="%1">%1 </a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished">Buscar actualizaciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2292,6 +2302,24 @@ Haga clic aquí: <a href="%1">%1 </a>
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1792,6 +1792,11 @@ Cliquez ici : <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Vérifier les mises à jour</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1812,6 +1817,11 @@ Cliquez ici : <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Vérifier les mises à jour</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2292,6 +2302,24 @@ Cliquez ici : <a href="%1">%1</a>
|
|||
<translation>Auto</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1782,6 +1782,11 @@ Kattintson ide: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Frissítések keresése</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1802,6 +1807,11 @@ Kattintson ide: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Frissítések keresése</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2279,6 +2289,24 @@ Kattintson ide: <a href="%1">%1</a>
|
|||
<translation>Önműködő</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1792,6 +1792,11 @@ Clicca: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Controlla aggiornamenti</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1812,6 +1817,11 @@ Clicca: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Controlla aggiornamenti</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2292,6 +2302,24 @@ Clicca: <a href="%1">%1</a>
|
|||
<translation>Automatico</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1782,6 +1782,11 @@
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1802,6 +1807,11 @@
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2279,6 +2289,24 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1802,6 +1802,11 @@ Spustelėkite čia: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1822,6 +1827,11 @@ Spustelėkite čia: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2305,6 +2315,24 @@ Spustelėkite čia: <a href="%1">%1</a>
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1792,6 +1792,11 @@ Clique aqui: <a href="%1">%1 </a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Verifique se há atualizações</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1812,6 +1817,11 @@ Clique aqui: <a href="%1">%1 </a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished">Verifique se há atualizações</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2292,6 +2302,24 @@ Clique aqui: <a href="%1">%1 </a>
|
|||
<translation>Auto</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1802,6 +1802,11 @@
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Проверить обновления</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1822,6 +1827,11 @@
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Проверить обновления</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2305,6 +2315,24 @@
|
|||
<translation>Авто</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1792,6 +1792,11 @@ Klicka här: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1812,6 +1817,11 @@ Klicka här: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2292,6 +2302,24 @@ Klicka här: <a href="%1">%1</a>
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1782,6 +1782,11 @@ Buraya tıklayın: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Güncellemeleri denetle</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1802,6 +1807,11 @@ Buraya tıklayın: <a href="%1">%1</a>
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation>Güncellemeleri denetle</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2279,6 +2289,24 @@ Buraya tıklayın: <a href="%1">%1</a>
|
|||
<translation>Kendiliğinden</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1802,6 +1802,11 @@
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1822,6 +1827,11 @@
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2305,6 +2315,24 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -1782,6 +1782,11 @@
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindowTopMenuBar</name>
|
||||
|
|
@ -1802,6 +1807,11 @@
|
|||
<extracomment>'Check for updates' : Item menu for checking updates</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordings</source>
|
||||
<extracomment>'Recordings' : Label for the recordings menu.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageAccounts</name>
|
||||
|
|
@ -2279,6 +2289,24 @@
|
|||
<translation>自动</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recordings</name>
|
||||
<message>
|
||||
<source>titleNoRecordings</source>
|
||||
<extracomment>'No recordings' : Title of an empty list of records.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsVocalLabel</source>
|
||||
<extracomment>'Vocal' : Label for recording type that is a vocal message.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>recordingsDelete</source>
|
||||
<extracomment>'Are you sure you want to delete this item?' : Confirmation message for removing a record.</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAdvanced</name>
|
||||
<message>
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@
|
|||
<file>assets/images/play_custom.svg</file>
|
||||
<file>assets/images/recording_sign.svg</file>
|
||||
<file>assets/images/record_custom.svg</file>
|
||||
<file>assets/images/recordings_custom.svg</file>
|
||||
<file>assets/images/remove_participant_custom.svg</file>
|
||||
<file>assets/images/screen_sharing_custom.svg</file>
|
||||
<file>assets/images/screenshot_custom.svg</file>
|
||||
|
|
@ -481,6 +482,7 @@
|
|||
<file>ui/views/App/Main/MainWindowMenuBar.qml</file>
|
||||
<file>ui/views/App/Main/MainWindow.qml</file>
|
||||
<file>ui/views/App/Main/MainWindowTopMenuBar.qml</file>
|
||||
<file>ui/views/App/Main/Recordings.qml</file>
|
||||
<file>ui/views/App/Settings/Dialogs/SettingsLdapEdit.qml</file>
|
||||
<file>ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.js</file>
|
||||
<file>ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml</file>
|
||||
|
|
@ -533,6 +535,7 @@
|
|||
<file>ui/views/App/Styles/Main/InviteFriendsStyle.qml</file>
|
||||
<file>ui/views/App/Styles/Main/HistoryViewStyle.qml</file>
|
||||
<file>ui/views/App/Styles/Main/MainWindowStyle.qml</file>
|
||||
<file>ui/views/App/Styles/Main/RecordingsStyle.qml</file>
|
||||
<file>ui/views/App/Styles/qmldir</file>
|
||||
<file>ui/views/App/Styles/Settings/Dialogs/SettingsSipAccountsEditStyle.qml</file>
|
||||
<file>ui/views/App/Styles/Settings/Dialogs/SettingsVideoPreviewStyle.qml</file>
|
||||
|
|
|
|||
|
|
@ -676,6 +676,7 @@ void App::registerTypes () {
|
|||
registerType<HistoryProxyModel>("HistoryProxyModel");
|
||||
registerType<LdapProxyModel>("LdapProxyModel");
|
||||
registerType<ParticipantImdnStateProxyModel>("ParticipantImdnStateProxyModel");
|
||||
registerType<RecordingProxyModel>("RecordingProxyModel");
|
||||
registerType<SipAddressesProxyModel>("SipAddressesProxyModel");
|
||||
registerType<SearchSipAddressesModel>("SearchSipAddressesModel");
|
||||
registerType<SearchSipAddressesProxyModel>("SearchSipAddressesProxyModel");
|
||||
|
|
@ -711,6 +712,7 @@ void App::registerTypes () {
|
|||
registerUncreatableType<ContactsImporterModel>("ContactsImporterModel");
|
||||
registerUncreatableType<ContentModel>("ContentModel");
|
||||
registerUncreatableType<ContentListModel>("ContentListModel");
|
||||
registerUncreatableType<FileMediaModel>("FileMediaModel");
|
||||
registerUncreatableType<HistoryModel>("HistoryModel");
|
||||
registerUncreatableType<LdapModel>("LdapModel");
|
||||
registerUncreatableType<RecorderModel>("RecorderModel");
|
||||
|
|
|
|||
|
|
@ -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<QObject> itemToRemove){
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include <QMutex>
|
||||
#include <QTimer>
|
||||
|
||||
#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;
|
||||
|
|
|
|||
151
linphone-app/src/components/file/FileMediaModel.cpp
Normal file
151
linphone-app/src/components/file/FileMediaModel.cpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "FileMediaModel.hpp"
|
||||
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
#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> FileMediaModel::create(const QString& path){
|
||||
return FileMediaModel::create(QFileInfo(path));
|
||||
}
|
||||
|
||||
QSharedPointer<FileMediaModel> FileMediaModel::create(const QFileInfo& fileInfo){
|
||||
auto model = QSharedPointer<FileMediaModel>::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
linphone-app/src/components/file/FileMediaModel.hpp
Normal file
75
linphone-app/src/components/file/FileMediaModel.hpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILE_MEDIA_MODEL_H_
|
||||
#define FILE_MEDIA_MODEL_H_
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
#include <QSharedPointer>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
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<FileMediaModel> create(const QString& path);
|
||||
static QSharedPointer<FileMediaModel> 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
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
78
linphone-app/src/components/recorder/RecordingListModel.cpp
Normal file
78
linphone-app/src/components/recorder/RecordingListModel.cpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 <QDebug>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
|
||||
// =============================================================================
|
||||
|
||||
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<QSharedPointer<FileMediaModel>> 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<FileMediaModel>(files);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> RecordingListModel::roleNames () const {
|
||||
QHash<int, QByteArray> 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<FileMediaModel>()->getCreationDateTime().date());
|
||||
}else
|
||||
return ProxyListModel::data(index, role);
|
||||
}
|
||||
45
linphone-app/src/components/recorder/RecordingListModel.hpp
Normal file
45
linphone-app/src/components/recorder/RecordingListModel.hpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<int, QByteArray> 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
|
||||
60
linphone-app/src/components/recorder/RecordingProxyModel.cpp
Normal file
60
linphone-app/src/components/recorder/RecordingProxyModel.cpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 <QDebug>
|
||||
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
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<RecordingListModel*>(sourceModel())->remove(fileModel);
|
||||
}
|
||||
|
||||
bool RecordingProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {
|
||||
const FileMediaModel* a = sourceModel()->data(left).value<FileMediaModel*>();
|
||||
const FileMediaModel* b = sourceModel()->data(right).value<FileMediaModel*>();
|
||||
|
||||
return a->getCreationDateTime() > b->getCreationDateTime();
|
||||
}
|
||||
43
linphone-app/src/components/recorder/RecordingProxyModel.hpp
Normal file
43
linphone-app/src/components/recorder/RecordingProxyModel.hpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RECORDING_PROXY_MODEL_H_
|
||||
#define RECORDING_PROXY_MODEL_H_
|
||||
|
||||
#include "app/proxyModel/SortFilterProxyModel.hpp"
|
||||
#include <memory>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
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
|
||||
|
|
@ -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<linphone::Player> SoundPlayer::getLinphonePlayer()const{
|
||||
return mInternalPlayer;
|
||||
}
|
||||
|
|
@ -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<linphone::Player> 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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
291
linphone-app/ui/views/App/Main/Recordings.qml
Normal file
291
linphone-app/ui/views/App/Main/Recordings.qml
Normal file
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
80
linphone-app/ui/views/App/Styles/Main/RecordingsStyle.qml
Normal file
80
linphone-app/ui/views/App/Styles/Main/RecordingsStyle.qml
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit e57d22dca56af8ecd9b0ee5b8e072dbdd65d2266
|
||||
Subproject commit d6c7561ffaecb43bce24e5f34c1a8e023fde6503
|
||||
Loading…
Add table
Reference in a new issue