VFS Encryption, QtKeychain and file viewer.

This commit is contained in:
Julien Wadel 2022-12-01 09:32:39 +01:00
parent 03abd9d972
commit 56cff70b1b
53 changed files with 2066 additions and 94 deletions

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@ path = linphone-sdk
[submodule "plugins/contacts/contacts-api"]
path = plugins/contacts/contacts-api
url = https://gitlab.linphone.org/BC/public/linphone-desktop-plugins/contacts/contacts-api.git
[submodule "external/qtkeychain"]
path = external/qtkeychain
url = https://gitlab.linphone.org/BC/public/external/qtkeychain.git

View file

@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 5.1.0 - undefined
### Added
- VFS Encryption
- File viewer in chats (Image/Animated Image/Video/Texts) with the option to export the file.
## 5.0.2 - 2022-12-13
### Fixed

View file

@ -54,7 +54,8 @@ set(CMAKE_CXX_STANDARD 11)
# Prepare gobal CMAKE configuration specific to the current project
set(SDK_BUILD_DIR "${CMAKE_BINARY_DIR}/WORK") # SDK build in WORK. Keep all in it.
set(LINPHONE_OUTPUT_DIR "${CMAKE_BINARY_DIR}/linphone-sdk/desktop")
set(QTKEYCHAIN_OUTPUT_DIR "${CMAKE_BINARY_DIR}/qtkeychain")
set(QTKEYCHAIN_TARGET_NAME "EQt5Keychain")
set(APPLICATION_OUTPUT_DIR "${CMAKE_BINARY_DIR}/OUTPUT")
set(CMAKE_PREFIX_PATH "${LINPHONE_OUTPUT_DIR};${APPLICATION_OUTPUT_DIR};${APPLICATION_OUTPUT_DIR}/include${PREFIX_PATH}")
@ -65,6 +66,8 @@ elseif(APPLE)
else()
set( CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${APPLICATION_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}")
endif()
list(APPEND CMAKE_PREFIX_PATH "${QTKEYCHAIN_OUTPUT_DIR}/lib64/cmake")
list(APPEND CMAKE_PREFIX_PATH "${QTKEYCHAIN_OUTPUT_DIR}/lib/cmake")
string(REPLACE ";" "|" PREFIX_PATH "${CMAKE_PREFIX_PATH}")
#set(PREFIX_PATH "${LINPHONE_OUTPUT_DIR}|${APPLICATION_OUTPUT_DIR}${PREFIX_PATH}")
@ -99,6 +102,7 @@ option(ENABLE_BUILD_EXAMPLES "Enable the build of examples" NO)
option(ENABLE_BUILD_VERBOSE "Enable the build generation to be more verbose" NO)
option(ENABLE_DAEMON "Enable the linphone daemon interface." NO)
option(ENABLE_FFMPEG "Build mediastreamer2 with ffmpeg video support." ON)
option(ENABLE_QT_KEYCHAIN "Build QtKeychain to manage VFS from System key stores." ON)
option(ENABLE_SANITIZER "Enable sanitizer." NO)
option(ENABLE_STRICT "Build with strict compilator flags e.g. -Wall -Werror" NO)
option(ENABLE_TESTS "Build with testing binaries of SDK" NO )
@ -127,6 +131,9 @@ endif()
option(ENABLE_RELATIVE_PREFIX "Set Internal packages relative to the binary" YES)
#QtKeychain
option(LIBSECRET_SUPPORT "Build with libsecret support" OFF) #Need libsecret-devel
set(APP_OPTIONS "-DENABLE_UPDATE_CHECK=${ENABLE_UPDATE_CHECK}")
list(APPEND APP_OPTIONS "-DENABLE_APP_LICENSE=${ENABLE_APP_LICENSE}")
list(APPEND APP_OPTIONS "-DENABLE_APP_PACKAGING=${ENABLE_APP_PACKAGING}")
@ -140,6 +147,7 @@ list(APPEND APP_OPTIONS "-DENABLE_FFMPEG=${ENABLE_FFMPEG}")
list(APPEND APP_OPTIONS "-DENABLE_LDAP=${ENABLE_LDAP}")
list(APPEND APP_OPTIONS "-DENABLE_NON_FREE_CODECS=${ENABLE_NON_FREE_CODECS}")
list(APPEND APP_OPTIONS "-DENABLE_OPENH264=${ENABLE_OPENH264}")
list(APPEND APP_OPTIONS "-DENABLE_QT_KEYCHAIN=${ENABLE_QT_KEYCHAIN}")
list(APPEND APP_OPTIONS "-DENABLE_SANITIZER=${ENABLE_SANITIZER}")
list(APPEND APP_OPTIONS "-DENABLE_STRICT=${ENABLE_STRICT}")
list(APPEND APP_OPTIONS "-DENABLE_TESTS=${ENABLE_TESTS}")
@ -148,9 +156,6 @@ list(APPEND APP_OPTIONS "-DENABLE_TOOLS=${ENABLE_TOOLS}")
list(APPEND APP_OPTIONS "-DENABLE_UNIT_TESTS=${ENABLE_UNIT_TESTS}")
list(APPEND APP_OPTIONS "-DENABLE_VIDEO=${ENABLE_VIDEO}")
if(LINPHONE_SDK_MAKE_RELEASE_FILE_URL)
list(APPEND APP_OPTIONS "-DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=${LINPHONE_SDK_MAKE_RELEASE_FILE_URL}")
endif()
@ -158,13 +163,15 @@ if(LINPHONESDK_MACOS_ARCHS)
list(APPEND APP_OPTIONS "-DLINPHONESDK_MACOS_ARCHS=${LINPHONESDK_MACOS_ARCHS}")
endif()
if(ENABLE_V4L)
list(APPEND APP_OPTIONS "-DENABLE_V4L=${ENABLE_V4L}")
endif()
list(APPEND APP_OPTIONS "-DENABLE_RELATIVE_PREFIX=${ENABLE_RELATIVE_PREFIX}")
list(APPEND APP_OPTIONS "-DLINPHONE_OUTPUT_DIR=${LINPHONE_OUTPUT_DIR}")
list(APPEND APP_OPTIONS "-DQTKEYCHAIN_OUTPUT_DIR=${QTKEYCHAIN_OUTPUT_DIR}")
list(APPEND APP_OPTIONS "-DQTKEYCHAIN_TARGET_NAME=${QTKEYCHAIN_TARGET_NAME}")
list(APPEND APP_OPTIONS "-DENABLE_QT_GL=${ENABLE_VIDEO}")#Activate on video
include(ExternalProject)
@ -192,6 +199,9 @@ if(CMAKE_OSX_DEPLOYMENT_TARGET)
endif()
list(APPEND APP_OPTIONS "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}")
set(QTKEYCHAIN_OPTIONS "-DLIBSECRET_SUPPORT=${LIBSECRET_SUPPORT}")
list(APPEND QTKEYCHAIN_OPTIONS "-DQTKEYCHAIN_TARGET_NAME=${QTKEYCHAIN_TARGET_NAME}")
if(ENABLE_BUILD_APP_PLUGINS)
file(GLOB children "plugins/*")
set(dirlist "")
@ -209,28 +219,43 @@ endif()
if(NOT LINPHONE_QT_ONLY)
ExternalProject_Add(sdk PREFIX "${CMAKE_BINARY_DIR}/sdk"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-sdk"
INSTALL_DIR "${LINPHONE_OUTPUT_DIR}"
STAMP_DIR "${SDK_BUILD_DIR}/stamp"
BINARY_DIR "${SDK_BUILD_DIR}"
STEP_TARGETS build
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> ${PROJECT_BUILD_COMMAND}
INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time."
LIST_SEPARATOR | # Use the alternate list separator
CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${PREFIX_PATH}
BUILD_ALWAYS NO #${DO_BUILD}
)
ExternalProject_Add_Step(sdk force_build
COMMENT "Forcing build for 'desktop'"
DEPENDEES configure
DEPENDERS build
ALWAYS 1
)
#add_subdirectory(external/qtkeychain)
if(ENABLE_QT_KEYCHAIN)
ExternalProject_Add(app-qtkeychain PREFIX "${CMAKE_BINARY_DIR}/qtkeychain"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/external/qtkeychain"
INSTALL_DIR "${QTKEYCHAIN_OUTPUT_DIR}"
BINARY_DIR "${SDK_BUILD_DIR}/qtkeychain"
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> ${PROJECT_BUILD_COMMAND}
LIST_SEPARATOR | # Use the alternate list separator
CMAKE_ARGS ${APP_OPTIONS} ${QTKEYCHAIN_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${PREFIX_PATH}
BUILD_ALWAYS NO #${DO_BUILD}
)
endif()
ExternalProject_Add(sdk PREFIX "${CMAKE_BINARY_DIR}/sdk"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-sdk"
INSTALL_DIR "${LINPHONE_OUTPUT_DIR}"
STAMP_DIR "${SDK_BUILD_DIR}/stamp"
BINARY_DIR "${SDK_BUILD_DIR}"
STEP_TARGETS build
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> ${PROJECT_BUILD_COMMAND}
INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time."
LIST_SEPARATOR | # Use the alternate list separator
CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${PREFIX_PATH}
BUILD_ALWAYS NO #${DO_BUILD}
)
ExternalProject_Add_Step(sdk force_build
COMMENT "Forcing build for 'sdk'"
DEPENDEES configure
DEPENDERS build
ALWAYS 1
)
endif()
include(FindPkgConfig)
set(APP_DEPENDS sdk)
set(APP_DEPENDS sdk)# Used if NOT LINPHONE_QT_ONLY
if(ENABLE_QT_KEYCHAIN)
list(APPEND APP_DEPENDS app-qtkeychain)
endif()
find_package(Qt5 5.10 COMPONENTS Core REQUIRED)
if ( NOT Qt5_FOUND )
@ -243,8 +268,12 @@ find_package(bctoolbox CONFIG QUIET)
find_package(belcard CONFIG QUIET)
find_package(Mediastreamer2 CONFIG QUIET)
find_package(ortp CONFIG QUIET)
find_package(${QTKEYCHAIN_TARGET_NAME} CONFIG QUIET)
if(NOT (LinphoneCxx_FOUND) OR NOT (Linphone_FOUND) OR NOT (bctoolbox_FOUND) OR NOT (belcard_FOUND) OR NOT (Mediastreamer2_FOUND) OR NOT (ortp_FOUND) OR FORCE_APP_EXTERNAL_PROJECTS)
if(NOT (LinphoneCxx_FOUND) OR NOT (Linphone_FOUND) OR NOT (bctoolbox_FOUND) OR NOT (belcard_FOUND) OR NOT (Mediastreamer2_FOUND) OR NOT (ortp_FOUND)
OR ( ENABLE_QT_KEYCHAIN AND NOT(${QTKEYCHAIN_TARGET_NAME}_FOUND) )
OR FORCE_APP_EXTERNAL_PROJECTS
)
message("Projects are set as External projects. You can start building them by using for example : cmake --build . --target install")
ExternalProject_Add(linphone-qt PREFIX "${CMAKE_BINARY_DIR}/linphone-app"
SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-app"
@ -270,7 +299,7 @@ if(NOT (LinphoneCxx_FOUND) OR NOT (Linphone_FOUND) OR NOT (bctoolbox_FOUND) OR N
CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${PREFIX_PATH}
)
endif()
install(CODE "message(STATUS Running install)")
install(CODE "message(STATUS \"Running install\")")
set(AUTO_REGENERATION auto_regeneration)
if( ENABLE_BUILD_APP_PLUGINS)
add_custom_target(${AUTO_REGENERATION} ALL

1
external/qtkeychain vendored Submodule

@ -0,0 +1 @@
Subproject commit bb47857aed7c3e9a66902ff681680a79f44e81e5

View file

@ -52,6 +52,11 @@ if(UNIX AND NOT APPLE)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake")
list(APPEND CMAKE_MODULE_PATH "${LINPHONE_OUTPUT_DIR}/cmake")
list(APPEND CMAKE_MODULE_PATH "${LINPHONE_OUTPUT_DIR}/lib64/cmake")
list(APPEND CMAKE_MODULE_PATH "${LINPHONE_OUTPUT_DIR}/lib/cmake")
list(APPEND CMAKE_PREFIX_PATH "${QTKEYCHAIN_OUTPUT_DIR}/lib64/cmake")
list(APPEND CMAKE_PREFIX_PATH "${QTKEYCHAIN_OUTPUT_DIR}/lib/cmake")
set(APP_LIBRARY app-library)
set(APP_PLUGIN app-plugin)
@ -82,6 +87,10 @@ find_package(belcard CONFIG)
find_package(Mediastreamer2 CONFIG)
find_package(ortp CONFIG)
if(ENABLE_QT_KEYCHAIN)
find_package(${QTKEYCHAIN_TARGET_NAME} CONFIG REQUIRED)
endif()
if(ENABLE_BUILD_VERBOSE)
message("INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} FRAMEWORK_PATH=${CMAKE_FRAMEWORK_PATH}, PREFIX_PATH=${CMAKE_PREFIX_PATH}")
message("LINPHONE : ${LINPHONE_INCLUDE_DIRS} => ${LINPHONE_LIBRARIES}")
@ -204,6 +213,7 @@ set(SOURCES
src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp
src/components/file/FileDownloader.cpp
src/components/file/FileExtractor.cpp
src/components/file/TemporaryFile.cpp
src/components/friend/FriendListListener.cpp
src/components/history/HistoryModel.cpp
src/components/history/HistoryProxyModel.cpp
@ -340,6 +350,7 @@ set(HEADERS
src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp
src/components/file/FileDownloader.hpp
src/components/file/FileExtractor.hpp
src/components/file/TemporaryFile.hpp
src/components/friend/FriendListListener.hpp
src/components/history/HistoryModel.hpp
src/components/history/HistoryProxyModel.hpp
@ -452,6 +463,11 @@ else ()
)
endif ()
if(ENABLE_QT_KEYCHAIN)
list(APPEND HEADERS src/components/vfs/VfsUtils.hpp)
list(APPEND SOURCES src/components/vfs/VfsUtils.cpp)
endif()
set(QRC_RESOURCES resources.qrc)
if(ENABLE_APP_WEBVIEW)
set(QRC_WEBVIEW_RESOURCES webview_resources.qrc)
@ -614,6 +630,12 @@ set(INCLUDED_DIRECTORIES "${LINPHONECXX_INCLUDE_DIRS}" "${MEDIASTREAMER2_INCLUDE
list(APPEND INCLUDED_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/include")
set(LIBRARIES_LIST ${BCTOOLBOX_CORE_LIBRARIES} ${BELCARD_LIBRARIES} ${LINPHONE_LIBRARIES} ${LINPHONECXX_LIBRARIES} ${MEDIASTREAMER2_LIBRARIES} ${ORTP_LIBRARIES} ${OPUS_LIBRARIES})
#if(ENABLE_QT_KEYCHAIN)
# list(APPEND INCLUDED_DIRECTORIES ${QtKeyChain_INCLUDE_DIRS})
# list(APPEND LIBRARIES_LIST ${QtKeyChain_LIBRARIES})
#endif()
if(WIN32)
set(LIBRARIES)
foreach(LIBRARY ${LIBRARIES_LIST})# Search for lib full path
@ -656,6 +678,12 @@ foreach (package ${QT5_PACKAGES_OPTIONAL})
endif ()
endforeach ()
if(ENABLE_QT_KEYCHAIN)
target_link_libraries(${APP_LIBRARY} ${QTKEYCHAIN_TARGET_NAME})
target_link_libraries(${APP_PLUGIN} ${QTKEYCHAIN_TARGET_NAME})
target_link_libraries(${TARGET_NAME} ${QTKEYCHAIN_TARGET_NAME})
endif()
if (APPLE)
list(APPEND LIBRARIES "-framework Cocoa -framework IOKit -framework AVFoundation")

View file

@ -1366,6 +1366,14 @@ Server url ikke konfigureret.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2356,6 +2364,44 @@ Klik her: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished">ANNULLER</translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3669,6 +3715,21 @@ Klik her: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -580,7 +580,7 @@ Server URL ist nicht konfiguriert.</translation>
<message>
<source>icsDescription</source>
<extracomment>&apos;Description&apos; : Title for the meeting description.</extracomment>
<translation type="unfinished"></translation>
<translation type="unfinished">Beschreibung</translation>
</message>
<message>
<source>icsJoinButton</source>
@ -1366,6 +1366,14 @@ Server URL ist nicht konfiguriert.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2356,6 +2364,44 @@ Klicken Sie hier: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3669,6 +3715,21 @@ Klicken Sie hier: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1366,6 +1366,14 @@ Server URL not configured.</translation>
<translation>In your app go in assistant - QR code provisioning</translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation>Export As</translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2356,6 +2364,44 @@ Click here: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<source>viewlogs</source>
<translation>VIEW</translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation>VFS</translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation>Encrypt all the application</translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation>Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.</translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation>Are you sure to activate the encryption? You cannot revert without deleting ALL your data.</translation>
</message>
<message>
<source>cancel</source>
<translation>Cancel</translation>
</message>
<message>
<source>confirm</source>
<translation>Confirm</translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation>The application will delete your application data files. Do you confirm ?</translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation>Delete data</translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3692,6 +3738,21 @@ Click here: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>To enable it in a commercial project, please contact us.</translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation>Delete key failed: %1</translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation>Read key failed: %1</translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation>Write key failed: %1</translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1366,6 +1366,14 @@ URL del servidor no configurada.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2356,6 +2364,44 @@ Haga clic aquí: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished">CANCELAR</translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3669,6 +3715,21 @@ Haga clic aquí: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -173,7 +173,7 @@
</message>
<message>
<source>usernameStatusInvalidCharacters</source>
<translation>Caractères invalides détectés (regex: `%1`).</translation>
<translation>Caractères invalides détectés (regex&#x202f;: `%1`).</translation>
</message>
<message>
<source>usernameStatusInvalid</source>
@ -189,7 +189,7 @@
</message>
<message>
<source>passwordStatusInvalidCharacters</source>
<translation>Caractères invalides détectés (regex: `%1`).</translation>
<translation>Caractères invalides détectés (regex&#x202f;: `%1`).</translation>
</message>
<message>
<source>passwordStatusMissingCharacters</source>
@ -1241,7 +1241,7 @@ URL du serveur non configurée.</translation>
<message>
<source>ephemeralNotInConference!</source>
<extracomment>&apos;Ephemeral message is only supported in conference based chat room!&apos;</extracomment>
<translation>Les messages éphémères ne sont disponibles que pour une conversation définie en mode conférence!</translation>
<translation>Les messages éphémères ne sont disponibles que pour une conversation définie en mode conférence&#x202f;!</translation>
<extra-Context>Warning about not being in conference based chat room.</extra-Context>
</message>
<message>
@ -1366,6 +1366,14 @@ URL du serveur non configurée.</translation>
<translation>Allez dans l&apos;assistant de l&apos;application - QR code</translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2025,7 +2033,7 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<message>
<source>newConferenceScheduleTitle</source>
<extracomment>&apos;Would you like to schedule your meeting?&apos; : Ask about setting the meeting as scheduled.</extracomment>
<translation>Voulez-vous programmer cette réunion?</translation>
<translation>Voulez-vous programmer cette réunion&#x202f;?</translation>
</message>
<message>
<source>newConferenceDate</source>
@ -2356,6 +2364,44 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<source>viewlogs</source>
<translation>AFFICHER</translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -2605,7 +2651,7 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
</message>
<message>
<source>serverTooltip</source>
<translation>Serveur LDAP. ie: ldap:// pour un serveur local ou ldap://ldap.example.org/</translation>
<translation>Serveur LDAP. ie&#x202f;: ldap:// pour un serveur local ou ldap://ldap.example.org/</translation>
</message>
<message>
<source>bindDNLabel</source>
@ -3551,7 +3597,7 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<message>
<source>deleteTimeline</source>
<extracomment>&apos;Are you sure you want to delete and leave this timeline?&apos;</extracomment>
<translation>Êtes-vous certain de vouloir tout effacer et de quitter cette conversation?</translation>
<translation>Êtes-vous certain de vouloir tout effacer et de quitter cette conversation&#x202f;?</translation>
</message>
<message>
<source>deleteTimelineTooltip</source>
@ -3669,6 +3715,21 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>Si vous souhaitez les activer pour un projet professionnel, contactez-nous.</translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1356,6 +1356,14 @@ A kiszolgáló URL-je nincs konfigurálva.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2343,6 +2351,44 @@ Kattintson ide: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished">Mégse</translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3656,6 +3702,21 @@ Kattintson ide: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1366,6 +1366,14 @@ URL del server non configurato.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2356,6 +2364,44 @@ Clicca: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<source>viewlogs</source>
<translation>VISTA</translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3669,6 +3715,21 @@ Clicca: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1356,6 +1356,14 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2343,6 +2351,44 @@
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3656,6 +3702,21 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1376,6 +1376,14 @@ Nesukonfigūruotas serverio url.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2369,6 +2377,44 @@ Spustelėkite čia: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished">ATSISAKYTI</translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3682,6 +3728,21 @@ Spustelėkite čia: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1366,6 +1366,14 @@ URL do servidor não configurado.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2356,6 +2364,44 @@ Clique aqui: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3669,6 +3715,21 @@ Clique aqui: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1376,6 +1376,14 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2369,6 +2377,44 @@
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished">ОТМЕНА</translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3682,6 +3728,21 @@
<translation>Чтобы включить их в коммерческом проекте, свяжитесь с нами.</translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1366,6 +1366,14 @@ Serverwebbadressen är inte konfigurerad.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2356,6 +2364,44 @@ Klicka här: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished">AVBRYT</translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3669,6 +3715,21 @@ Klicka här: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1356,6 +1356,14 @@ Sunucu url&apos;si yapılandırılmadı.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2343,6 +2351,44 @@ Buraya tıklayın: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3656,6 +3702,21 @@ Buraya tıklayın: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>Ticari projede etkinleştirmek için lütfen bizimle iletişime geçin.</translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1376,6 +1376,14 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2369,6 +2377,44 @@
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished">СКАСУВАТИ</translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3682,6 +3728,21 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -1356,6 +1356,14 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileViewDialog</name>
<message>
<source>exportAsTitle</source>
<extracomment>&quot;Export As...&quot;: Title of a file dialog to export a file.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>HistoryView</name>
<message>
@ -2343,6 +2351,44 @@
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsTitle</source>
<extracomment>&apos;VFS&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsEncryption</source>
<extracomment>&apos;Encrypt all the application&apos; : Label to encrypt application</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>deleteData</source>
<extracomment>&apos;Delete data&apos; : Action to delete all data.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>vfsDeletion</source>
<extracomment>&apos;The application will delete your application data files. Do you confirm ?&apos;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>
@ -3656,6 +3702,21 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WaitingRoom</name>
<message>

View file

@ -109,6 +109,10 @@ endif ()
# ==============================================================================
set(APP_QT_CONF_DPI "1")
if (WIN32)
if(ENABLE_QT_KEYCHAIN)
file(GLOB LIB_FILES "${QTKEYCHAIN_OUTPUT_DIR}/${CMAKE_INSTALL_BINDIR}/*.dll")
install(FILES ${LIB_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/")
endif()
file(GLOB LIB_FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}/*.dll")
install(FILES ${LIB_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/")
@ -214,6 +218,10 @@ elseif (APPLE)
message("Changing RPATH of ${LIBRARY_FILENAME} from '${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}' to '@executable_path/../Frameworks'")
execute_process(COMMAND install_name_tool -rpath "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}" "@executable_path/../Frameworks" "${LIBRARY}")
endforeach ()
if(ENABLE_QT_KEYCHAIN)
file(GLOB SHARED_LIBRARIES "${QTKEYCHAIN_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}/lib*.dylib")
install(FILES ${SHARED_LIBRARIES} DESTINATION "${APPLICATION_NAME}.app/Contents/Frameworks")
endif()
install(TARGETS ${APP_PLUGIN}
ARCHIVE DESTINATION "${APPLICATION_NAME}.app/Contents/Frameworks"
LIBRARY DESTINATION "${APPLICATION_NAME}.app/Contents/Frameworks"
@ -242,6 +250,9 @@ else()# Not Windows and Apple
execute_process(COMMAND install_name_tool -addrpath "$ORIGIN/../lib64" "${LIBRARY}")
endforeach ()
install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_BINDIR}/" DESTINATION "${CMAKE_INSTALL_BINDIR}" USE_SOURCE_PERMISSIONS)
if(ENABLE_QT_KEYCHAIN)
install(DIRECTORY "${QTKEYCHAIN_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}/" DESTINATION "${CMAKE_INSTALL_LIBDIR}" USE_SOURCE_PERMISSIONS)
endif()
#Just in case. This is useless because we have to use CMAKE_INSTALL_LIBDIR
if( EXISTS "${LINPHONE_OUTPUT_DIR}/lib/")
file(GLOB SHARED_LIBRARIES "${LINPHONE_OUTPUT_DIR}/lib/*.so*")

View file

@ -175,6 +175,7 @@
<file>ui/modules/Common/Dialog/DialogDescription.qml</file>
<file>ui/modules/Common/Dialog/DialogPlus.qml</file>
<file>ui/modules/Common/Dialog/DialogTitle.qml</file>
<file>ui/modules/Common/Dialog/FileViewDialog.qml</file>
<file>ui/modules/Common/Form/ActionBar.qml</file>
<file>ui/modules/Common/Form/ActionButton.qml</file>
<file>ui/modules/Common/Form/ActionSwitch.qml</file>
@ -223,6 +224,7 @@
<file>ui/modules/Common/Form/Tab/TabContainer.qml</file>
<file>ui/modules/Common/Form/TransparentTextInput.qml</file>
<file>ui/modules/Common/Helpers/DragBox.qml</file>
<file>ui/modules/Common/Helpers/HoveringMouseArea.qml</file>
<file>ui/modules/Common/Helpers/InvertedMouseArea.qml</file>
<file>ui/modules/Common/Image/Icon.qml</file>
<file>ui/modules/Common/Image/RoundedImage.qml</file>
@ -249,6 +251,7 @@
<file>ui/modules/Common/Styles/Animations/BusyIndicatorStyle.qml</file>
<file>ui/modules/Common/Styles/Dialog/DateTimeDialogStyle.qml</file>
<file>ui/modules/Common/Styles/Dialog/DialogStyle.qml</file>
<file>ui/modules/Common/Styles/Dialog/FileViewDialogStyle.qml</file>
<file>ui/modules/Common/Styles/Form/ActionBarStyle.qml</file>
<file>ui/modules/Common/Styles/Form/ActionSwitchStyle.qml</file>
<file>ui/modules/Common/Styles/Form/Buttons/AbstractTextButtonStyle.qml</file>

View file

@ -675,6 +675,7 @@ void App::registerTypes () {
registerType<SipAddressesProxyModel>("SipAddressesProxyModel");
registerType<SearchSipAddressesModel>("SearchSipAddressesModel");
registerType<SearchSipAddressesProxyModel>("SearchSipAddressesProxyModel");
registerType<TemporaryFile>("TemporaryFile");
registerType<TimeZoneProxyModel>("TimeZoneProxyModel");
registerType<ColorProxyModel>("ColorProxyModel");

View file

@ -105,6 +105,7 @@ public:
}
static constexpr int RestartCode = 1000;
static constexpr int DeleteDataCode = 1001;
Q_INVOKABLE void restart () {
exit(RestartCode);

View file

@ -30,6 +30,9 @@ FILE * gStream = NULL;
#endif
#include "components/core/CoreManager.hpp"
#include "components/vfs/VfsUtils.hpp"
#include "utils/Utils.hpp"
// =============================================================================
void cleanStream(){
@ -43,6 +46,7 @@ void cleanStream(){
}
int main (int argc, char *argv[]) {
#ifdef __APPLE__
qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "1"); // On Mac, set this workaround to avoid glitches on M1, because of https://bugreports.qt.io/browse/QTBUG-89379
@ -53,13 +57,16 @@ int main (int argc, char *argv[]) {
freopen_s(&gStream, "CONOUT$", "w", stderr);
}
#endif
bool vfsEncrypted = VfsUtils::updateSDKWithKey();
AppController controller(argc, argv);
#ifdef QT_QML_DEBUG
QQmlDebuggingEnabler enabler;
#endif
//QLoggingCategory::setFilterRules("*.debug=true;qml=false");
App *app = controller.getApp();
if(vfsEncrypted)
qInfo() << "Activation of VFS encryption.";
if (app->isSecondary())
{
qInfo() << QStringLiteral("Running secondary app success. Kill it now.");
@ -81,5 +88,8 @@ int main (int argc, char *argv[]) {
core->stop();
}
cleanStream();
if( ret == App::DeleteDataCode){
Utils::deleteAllUserDataOffline();
}
return ret;
}

View file

@ -36,7 +36,7 @@ ExternalImageProvider::ExternalImageProvider () : QQuickImageProvider(
}
QImage ExternalImageProvider::requestImage (const QString &id, QSize *size, const QSize &) {
QImage image(Utils::getImage(id));
QImage image(Utils::getImage(QUrl::fromPercentEncoding(id.toUtf8())));
*size = image.size();
return image;
}

View file

@ -54,6 +54,7 @@
#include "core/CoreManager.hpp"
#include "file/FileDownloader.hpp"
#include "file/FileExtractor.hpp"
#include "file/TemporaryFile.hpp"
#include "history/HistoryProxyModel.hpp"
#include "ldap/LdapModel.hpp"
#include "ldap/LdapListModel.hpp"

View file

@ -294,5 +294,30 @@ void ContentModel::openFile (bool showDirectory) {
}
}
bool ContentModel::saveAs (const QString& path){
QString cachePath = Utils::coreStringToAppString(mContent->exportPlainFile());
bool toDelete = true;
bool result = false;
if(cachePath.isEmpty()) {
cachePath = Utils::coreStringToAppString(mContent->getFilePath());
toDelete = false;
}
if(!cachePath.isEmpty())
{
QString decodedPath = QUrl::fromPercentEncoding(path.toUtf8());
QFile file(cachePath);
QFile newFile(decodedPath);
if(newFile.exists())
newFile.remove();
result = file.copy(decodedPath);
if(toDelete)
file.remove();
if(result)
QDesktopServices::openUrl(QUrl(QStringLiteral("file:///%1").arg(decodedPath)));
}
return result;
}
void ContentModel::updateTransferData(){
}

View file

@ -82,6 +82,7 @@ public:
Q_INVOKABLE void downloadFile();
Q_INVOKABLE void cancelDownloadFile();
Q_INVOKABLE void openFile (bool showDirectory = false);
Q_INVOKABLE bool saveAs (const QString& path);
QString mThumbnail;

View file

@ -56,7 +56,7 @@ void ContentProxyModel::setContentListModel(ContentListModel * model){
void ContentProxyModel::addFile(const QString& path){
ContentListModel* model = qobject_cast<ContentListModel*>(sourceModel());
model->addFile(path);
model->addFile( QUrl::fromPercentEncoding(path.toUtf8()));
}
bool ContentProxyModel::filterAcceptsRow (

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2010-2020 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 <QDebug>
#include "TemporaryFile.hpp"
#include "components/core/CoreManager.hpp"
#include "components/content/ContentModel.hpp"
#include "components/settings/SettingsModel.hpp"
#include "utils/Utils.hpp"
// =============================================================================
using namespace std;
TemporaryFile::TemporaryFile (QObject *parent) : QObject(parent) {
}
TemporaryFile::~TemporaryFile () {
deleteFile();
}
void TemporaryFile::createFileFromContent(ContentModel * contentModel, const bool& exportPlainFile){
if(contentModel){
QString filePath;
if( exportPlainFile || CoreManager::getInstance()->getSettingsModel()->getVfsEncrypted() )
filePath = Utils::coreStringToAppString(contentModel->getContent()->exportPlainFile());
bool toDelete = true;
if(filePath.isEmpty()){
filePath = contentModel->getFilePath();
toDelete = false;
}
setFilePath(filePath, toDelete);
}
}
QString TemporaryFile::getFilePath () const{
return mFilePath;
}
void TemporaryFile::setFilePath(const QString& path, const bool& toDelete){
if(path != mFilePath) {
deleteFile();
mFilePath = path;
mDeleteFile = toDelete;
emit filePathChanged();
}
}
void TemporaryFile::deleteFile(){
if(mDeleteFile && !mFilePath.isEmpty())
QFile::remove(mFilePath);
mFilePath = "";
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2010-2020 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 TEMPORARY_FILE_H_
#define TEMPORARY_FILE_H_
#include <QFile>
// =============================================================================
class ContentModel;
class TemporaryFile : public QObject {
Q_OBJECT
public:
TemporaryFile (QObject *parent = nullptr);
~TemporaryFile ();
Q_PROPERTY(QString filePath READ getFilePath NOTIFY filePathChanged)// not changeable from QML as it comes from a ContentModel
Q_INVOKABLE void createFileFromContent(ContentModel * contentModel, const bool& exportPlainFile = true);
QString getFilePath () const;
void setFilePath(const QString& path, const bool& toDelete);
void deleteFile();
signals :
void filePathChanged();
private:
QString mFilePath;
bool mDeleteFile = false;
};
#endif

View file

@ -285,7 +285,7 @@ class ColorListModel : public ProxyListModel {
// Field error.
ADD_COLOR("error", "#FF0000", "Error Generic button.")
ADD_COLOR_WITH_ALPHA("c", 80, "")
ADD_COLOR_WITH_ALPHA("g", 10, "")
ADD_COLOR_WITH_ALPHA("g", 20, "")
ADD_COLOR_WITH_ALPHA("g", 90, "")

View file

@ -69,6 +69,38 @@ SettingsModel::SettingsModel (QObject *parent) : QObject(parent) {
connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::accountSettingsUpdated, this, &SettingsModel::videoConferenceEnabledChanged);
connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::accountSettingsUpdated, this, &SettingsModel::secureChatEnabledChanged);
connect(&mVfsUtils, &VfsUtils::keyRead, this, [&](const QString& key, const QString& value){
if(key == mVfsUtils.getApplicationVfsEncryptionKey()){
if(!mVfsEncrypted){
mVfsEncrypted = true;
emit vfsEncryptedChanged();
}
}
});
connect(&mVfsUtils, &VfsUtils::keyWritten, this, [&](const QString& key){
if(key == mVfsUtils.getApplicationVfsEncryptionKey()){
if(!mVfsEncrypted){
mVfsEncrypted = true;
emit vfsEncryptedChanged();
}
}
});
connect(&mVfsUtils, &VfsUtils::keyDeleted, this, [&](const QString& key){
if(key == mVfsUtils.getApplicationVfsEncryptionKey()){
mVfsEncrypted = false;
emit vfsEncryptedChanged();
if(mVfsUtils.needToDeleteUserData())
Utils::deleteAllUserData();
else
App::getInstance()->quit();
}
});
connect(&mVfsUtils, &VfsUtils::error, this, [&](){
});
configureRlsUri();
}
@ -1697,6 +1729,25 @@ bool SettingsModel::getLogsEnabled (const shared_ptr<linphone::Config> &config)
}
// ---------------------------------------------------------------------------
bool SettingsModel::getVfsEncrypted (){
mVfsUtils.readKey(mVfsUtils.getApplicationVfsEncryptionKey());
return mVfsEncrypted;
}
void SettingsModel::setVfsEncrypted (bool encrypted, const bool deleteUserData){
if(getVfsEncrypted() != encrypted){
if(encrypted) {
mVfsUtils.newEncryptionKey();
}else{// Remove key, stop core, delete data and initiate reboot
mVfsUtils.needToDeleteUserData(deleteUserData);
mVfsUtils.deleteKey(mVfsUtils.getApplicationVfsEncryptionKey());
}
}
}
// ---------------------------------------------------------------------------
bool SettingsModel::isDeveloperSettingsAvailable() const {
#ifdef DEBUG
return true;

View file

@ -29,6 +29,7 @@
#include "components/core/CoreHandlers.hpp"
#include "components/contacts/ContactsImporterModel.hpp"
#include "components/vfs/VfsUtils.hpp"
#include "utils/LinphoneEnums.hpp"
// =============================================================================
@ -221,6 +222,8 @@ class SettingsModel : public QObject {
Q_PROPERTY(bool logsEnabled READ getLogsEnabled WRITE setLogsEnabled NOTIFY logsEnabledChanged)
Q_PROPERTY(QString logsEmail READ getLogsEmail WRITE setLogsEmail NOTIFY logsEmailChanged)
Q_PROPERTY(bool isVfsEncrypted READ getVfsEncrypted NOTIFY vfsEncryptedChanged)
Q_PROPERTY(bool developerSettingsEnabled READ getDeveloperSettingsEnabled WRITE setDeveloperSettingsEnabled NOTIFY developerSettingsEnabledChanged)
Q_PROPERTY(bool isInCall READ getIsInCall NOTIFY isInCallChanged)
@ -610,6 +613,9 @@ public:
QString getLogsEmail () const;
void setLogsEmail (const QString &email);
bool getVfsEncrypted ();
Q_INVOKABLE void setVfsEncrypted (bool encrypted, const bool deleteUserData);
Q_INVOKABLE bool isLdapAvailable();
// ---------------------------------------------------------------------------
@ -797,6 +803,7 @@ signals:
void logsUploadUrlChanged (const QString &url);
void logsEnabledChanged (bool status);
void logsEmailChanged (const QString &email);
void vfsEncryptedChanged();
void contactImporterChanged();
@ -808,6 +815,8 @@ private:
int mCurrentSettingsTab = 0;
MediastreamerUtils::SimpleCaptureGraph *mSimpleCaptureGraph = nullptr;
int mCaptureGraphListenerCount = 0;
VfsUtils mVfsUtils;
bool mVfsEncrypted = false;
std::shared_ptr<linphone::Config> mConfig;
};

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2010-2022 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 "VfsUtils.hpp"
#include <bctoolbox/crypto.hh>
#include <linphone/api/c-factory.h>
#include <linphone++/factory.hh>
#include <utils/Utils.hpp>
#include <utils/Constants.hpp>
#include <QCoreApplication>
#include <QDebug>
// =============================================================================
VfsUtils::VfsUtils (QObject *parent) : QObject(parent)
, mReadCredentialJob(QLatin1String(APPLICATION_ID))
, mWriteCredentialJob(QLatin1String(APPLICATION_ID))
, mDeleteCredentialJob(QLatin1String(APPLICATION_ID))
{
mReadCredentialJob.setAutoDelete(false);
mWriteCredentialJob.setAutoDelete(false);
mDeleteCredentialJob.setAutoDelete(false);
}
void VfsUtils::deleteKey(const QString &key){
mDeleteCredentialJob.setKey(key);
QObject::connect(&mDeleteCredentialJob, &QKeychain::DeletePasswordJob::finished, [=](){
if (mDeleteCredentialJob.error()) {
emit error(tr("Delete key failed: %1").arg(qPrintable(mDeleteCredentialJob.errorString())));
return;
}
emit keyDeleted(key);
});
mDeleteCredentialJob.start();
}
void VfsUtils::readKey(const QString &key) {
mReadCredentialJob.setKey(key);
QObject::connect(&mReadCredentialJob, &QKeychain::ReadPasswordJob::finished, [=](){
if (mReadCredentialJob.error()) {
emit error(tr("Read key failed: %1").arg(qPrintable(mReadCredentialJob.errorString())));
return;
}
emit keyRead(key, mReadCredentialJob.textData());
});
mReadCredentialJob.start();
}
void VfsUtils::writeKey(const QString &key, const QString &value) {
mWriteCredentialJob.setKey(key);
QObject::connect(&mWriteCredentialJob, &QKeychain::WritePasswordJob::finished, [=](){
if (mWriteCredentialJob.error()) {
emit error(tr("Write key failed: %1").arg(qPrintable(mWriteCredentialJob.errorString())));
return;
}
if(key == getApplicationVfsEncryptionKey())
updateSDKWithKey(value);
emit keyWritten(key);
});
mWriteCredentialJob.setTextData(value);
mWriteCredentialJob.start();
}
bool VfsUtils::needToDeleteUserData() const{
return mNeedToDeleteUserData;
}
void VfsUtils::needToDeleteUserData(const bool& need){
mNeedToDeleteUserData = need;
}
//-----------------------------------------------------------------------------------------------
void VfsUtils::newEncryptionKey(){
QString value;
bctoolbox::RNG rng;
auto key = rng.randomize(32);
size_t keySize = key.size();
uint8_t * shaKey = new uint8_t[keySize];
bctbx_sha256(&key[0], key.size(), keySize, shaKey);
for(int i = 0 ; i < keySize ; ++i)
value += QString::number(shaKey[i], 16);
writeKey(getApplicationVfsEncryptionKey(), value);
}
bool VfsUtils::updateSDKWithKey() {
int argc = 1;
const char * argv = "dummy";
QCoreApplication vfsSetter(argc,(char**)&argv);
VfsUtils vfs;
QObject::connect(&vfs, &VfsUtils::keyRead, &vfsSetter, [&vfsSetter, &vfs] (const QString& key, const QString& value){
VfsUtils::updateSDKWithKey(value);
vfs.mVfsEncrypted = true;
vfsSetter.quit();
}, Qt::QueuedConnection);
QObject::connect(&vfs, &VfsUtils::error, &vfsSetter, [&vfsSetter](){
vfsSetter.quit();
}, Qt::QueuedConnection);
vfs.readKey(vfs.getApplicationVfsEncryptionKey());
vfsSetter.exec();
return vfs.mVfsEncrypted;
}
void VfsUtils::updateSDKWithKey(const QString& key){
std::string value = Utils::appStringToCoreString(key);
linphone::Factory::get()->setVfsEncryption(LINPHONE_VFS_ENCRYPTION_AES256GCM128_SHA256, (const uint8_t*)value.c_str(), std::min(32, (int)value.length()));
}
QString VfsUtils::getApplicationVfsEncryptionKey() const{
return QString(APPLICATION_ID)+"VfsEncryption";
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2010-2022 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 VFS_UTILS_H_
#define VFS_UTILS_H_
#include <QObject>
#include <EQt5Keychain/keychain.h>
// =============================================================================
class VfsUtils : public QObject {
Q_OBJECT
public:
VfsUtils(QObject *parent = Q_NULLPTR);
Q_INVOKABLE void deleteKey(const QString& key); // Delete a key and send error() or keyDeleted()
Q_INVOKABLE void readKey(const QString& key); // Read a key, send error() or keyStored()
Q_INVOKABLE void writeKey(const QString& key, const QString& value); // Write a key and send error() or keyWritten()
void newEncryptionKey(); // Generate a key, store it and update SDK.
static bool updateSDKWithKey(); // Update SDK if key exists. Return true if encrypted.
static void updateSDKWithKey(const QString& key);// SDK->setVfsEncryption(key)
QString getApplicationVfsEncryptionKey() const;// Get the key in store keys for VFS encryyption
bool needToDeleteUserData() const;
void needToDeleteUserData(const bool& need);
signals:
void keyDeleted(const QString& key);
void keyRead(const QString& key, const QString& value);
void keyWritten(const QString& key);
void error(const QString& errorText);
private:
QKeychain::ReadPasswordJob mReadCredentialJob;
QKeychain::WritePasswordJob mWriteCredentialJob;
QKeychain::DeletePasswordJob mDeleteCredentialJob;
bool mNeedToDeleteUserData = false;
bool mVfsEncrypted = false;
};
#endif

View file

@ -26,12 +26,14 @@
#include <QFile>
#include <QImageReader>
#include <QDebug>
#include <QMimeDatabase>
#include <QTimeZone>
#include <QUrl>
#include "config.h"
#include "Utils.hpp"
#include "UriTools.hpp"
#include "app/App.hpp"
#include "components/core/CoreManager.hpp"
#include "components/contacts/ContactsListModel.hpp"
#include "components/contact/ContactModel.hpp"
@ -566,6 +568,23 @@ bool Utils::isAnimatedImage(const QString& path){
return reader.supportsAnimation() && reader.imageCount() > 1;
}
bool Utils::isImage(const QString& path){
QFileInfo info(path);
if( !info.exists())
return false;
QImageReader reader(path);
return reader.imageCount() == 1;
}
bool Utils::isVideo(const QString& path){
return QMimeDatabase().mimeTypeForFile(path).name().contains("video");
}
bool Utils::isSupportedForDisplay(const QString& path){
return !QMimeDatabase().mimeTypeForFile(path).name().contains("application");// "for pdf : "application/pdf". Note : Make an exception when supported.
}
bool Utils::isPhoneNumber(const QString& txt){
auto core = CoreManager::getInstance()->getCore();
if(!core)
@ -647,4 +666,30 @@ QString Utils::encodeTextToQmlRichFormat(const QString& text, const QVariantMap&
images = "<div>" + images +"</div>";
return images + "<p style=\"white-space:pre-wrap;\">" + formattedText.join("") + "</p>";
}
QString Utils::getFileContent(const QString& filePath){
QString contents;
QFile file(filePath);
if (!file.open(QFile::ReadOnly | QFile::Text))
return "";
return file.readAll();
}
static QStringList gDbPaths;
void Utils::deleteAllUserData(){
// Store usable data like custom folders
gDbPaths.clear();
gDbPaths.append(Utils::coreStringToAppString(linphone::Factory::get()->getDataDir(nullptr)));
gDbPaths.append(Utils::coreStringToAppString(linphone::Factory::get()->getConfigDir(nullptr)));
// Exit with a delete code
App::getInstance()->exit(App::DeleteDataCode);
}
void Utils::deleteAllUserDataOffline(){
qWarning() << "Deleting all data! ";
for(int i = 0 ; i < gDbPaths.size() ; ++i){
QDir dir(gDbPaths[i]);
qWarning() << "Deleting " << gDbPaths[i] << " : " << (dir.removeRecursively() ? "Successfully" : "Failed");
}
}

View file

@ -62,11 +62,15 @@ public:
Q_INVOKABLE static QString toString(const LinphoneEnums::TunnelMode& mode);
Q_INVOKABLE static bool isMe(const QString& address);
Q_INVOKABLE static bool isAnimatedImage(const QString& path);
Q_INVOKABLE static bool isImage(const QString& path);
Q_INVOKABLE static bool isVideo(const QString& path);
Q_INVOKABLE static bool isSupportedForDisplay(const QString& path);
Q_INVOKABLE static bool isPhoneNumber(const QString& txt);
Q_INVOKABLE QSize getImageSize(const QString& url);
Q_INVOKABLE static QPoint getCursorPosition();
Q_INVOKABLE static QString getFileChecksum(const QString& filePath);
Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString& text, const QVariantMap& options);
Q_INVOKABLE static QString getFileContent(const QString& filePath);
//----------------------------------------------------------------------------------
@ -146,6 +150,9 @@ public:
static bool isMe(const std::shared_ptr<const linphone::Address>& address);
static void deleteAllUserData();
static void deleteAllUserDataOffline();// When we are out of all events and core is not running (aka in main())
};
#endif // UTILS_H_

View file

@ -23,11 +23,16 @@ DialogPlus {
text: mainItem.buttonTexts[1]
visible: mainItem.showButtonOnly<0 || mainItem.showButtonOnly == 1
onClicked: exit(1)
},
TextButtonB {
text: mainItem.buttonTexts.length > 2 ? mainItem.buttonTexts[2] : ''
visible: mainItem.buttonTexts.length > 2 && (mainItem.showButtonOnly<0 || mainItem.showButtonOnly == 2)
onClicked: exit(2)
}
]
buttonsAlignment: Qt.AlignCenter
height: DialogStyle.confirmDialog.height + 30
width: DialogStyle.confirmDialog.width
width: Math.max(DialogStyle.confirmDialog.width, buttonTexts.length * 150 + DialogStyle.buttons.leftMargin + DialogStyle.buttons.rightMargin)
}

View file

@ -21,6 +21,7 @@ Rectangle {
property bool expandHeight: flat
property alias showCloseCross : titleBar.showCloseCross
property alias showTitleBar: titleBar.showBar
property alias showButtons: buttonsView.visible
property int buttonsLeftMargin :(buttonsAlignment & Qt.AlignLeft )== Qt.AlignLeft
? DialogStyle.buttons.leftMargin

View file

@ -0,0 +1,286 @@
import QtQuick 2.12
import QtQuick.Controls 2.15
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.3
import QtMultimedia 5.15
import Common 1.0
import Linphone 1.0
import Utils 1.0
import App.Styles 1.0
import Common.Styles 1.0
import Linphone.Styles 1.0
import UtilsCpp 1.0
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
// =============================================================================
DialogPlus{
id: mainItem
property ContentModel contentModel
property string filePath: tempFile.filePath
property bool isAnimatedImage : filePath && UtilsCpp.isAnimatedImage(filePath)
property bool isVideo: filePath && UtilsCpp.isVideo(filePath)
property bool isImage: filePath && UtilsCpp.isImage(filePath)
property bool isSupportedForDisplay: filePath && UtilsCpp.isSupportedForDisplay(filePath)
showCloseCross: true
showButtons: !isVideo
buttonsAlignment: Qt.AlignRight
buttons: [
ActionButton{
Layout.preferredHeight: iconSize
Layout.preferredWidth: iconSize
isCustom: true
backgroundRadius: width
colorSet: FileViewDialogStyle.exportFile
onClicked: saveAsDialog.open()
}
]
height: window.height - 20
width: window.width - 20
expandHeight: true
radius: 10
onContentModelChanged: if(contentModel){
tempFile.createFileFromContent(contentModel, false);
}
onExitStatus: if(loader.sourceComponent == videoComponent) loader.item.stop();
TemporaryFile {
id: tempFile
}
FileDialog {
id: saveAsDialog
folder: shortcuts.documents
//: "Export As...": Title of a file dialog to export a file.
title: qsTr('exportAsTitle')
selectExisting: false
defaultSuffix: Utils.getExtension(mainItem.filePath)// Doesn't seems to work on all platforms
onAccepted: {
var files = fileUrls.reduce(function (files, file) {
if (file.startsWith('file:')) {
files.push(Utils.getSystemPathFromUri(file))
}
return files
}, [])
contentModel.saveAs(files[0])
}
}
Loader{
id: loader
anchors.fill: parent
active: true
sourceComponent: isVideo
? videoComponent
: mainItem.isAnimatedImage
? animatedImageComponent
: mainItem.isImage
? imageComponent
: isSupportedForDisplay
? fileTextComponent
: placeholderComponent
//--------------------------------------------------------------------------------------------------
// VIDEOS
//--------------------------------------------------------------------------------------------------
Component{
id: videoComponent
Video{
id: videoItem
property bool isPlaying: videoItem.playbackState == MediaPlayer.PlayingState
anchors.fill: parent
source: 'file:'+mainItem.filePath // GStreamer doesn't know 'file://'
autoPlay: true
//loops: MediaPlayer.Infinite// Do not use because MediaPlayer can crash while trying to replay video.
flushMode: VideoOutput.FirstFrame
notifyInterval: 100
onStatusChanged: {
if(MediaPlayer.EndOfMedia == status){// Workaround for a Qt crash when replaying a video after reaching the End of the Video.
console.info("Closing the popup at the end is a workaround to avoid a crash from Qt(5.15.2)")
mainItem.exit()
}
}
BusyIndicator{
visible: videoItem.playbackState == MediaPlayer.StoppedState
anchors.centerIn: parent
height: 50
width: 50
color: BusyIndicatorStyle.alternateColor
}
HoveringMouseArea{
id: hoveringMouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: Qt.ArrowCursor
onClicked: videoControl.forceVisible = !videoControl.forceVisible
Item{
id: videoControl
property bool forceVisible: false
property bool autoHide: false
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: 20
anchors.rightMargin: 20
anchors.bottomMargin: 0
height: 50
visible: forceVisible || !videoControl.autoHide || hoveringMouseArea.realRunning
Component.onCompleted: autoHideDelayer.start()
Timer{
id: autoHideDelayer
interval: 1000
onTriggered: videoControl.autoHide = true
}
RowLayout{
anchors.fill: parent
MediaProgressBar{
id: mediaProgressBar
Layout.fillHeight: true
Layout.fillWidth: true
progressDuration: videoItem.duration
progressPosition: videoItem.position
stopAtEnd: false
resetAtEnd: false
blockValueAtEnd: false
backgroundColor: ChatAudioMessageStyle.backgroundColor
progressLineBackgroundColor: FileViewDialogStyle.progression.backgroundColor
colorSet: FileViewDialogStyle.progression
durationTextColor: ChatStyle.entry.message.outgoing.text.color
progressSize: 10
customActions: [
ActionButton{
Layout.preferredHeight: iconSize
Layout.preferredWidth: iconSize
Layout.leftMargin: 15
Layout.rightMargin: 5
isCustom: true
backgroundRadius: width
colorSet: FileViewDialogStyle.exportFile
onClicked: {
if(videoItem.isPlaying)
videoItem.pause()
saveAsDialog.open()
}
},
ActionButton{
id: playButton
Layout.preferredHeight: iconSize
Layout.preferredWidth: iconSize
Layout.rightMargin: 5
Layout.leftMargin: 5
Layout.alignment: Qt.AlignVCenter
isCustom: true
backgroundRadius: width
colorSet: (videoItem.isPlaying ? FileViewDialogStyle.pauseAction
: FileViewDialogStyle.playAction)
onClicked: videoItem.isPlaying ? videoItem.pause() : videoItem.play()
},
ActionButton{
id: muteButton
Layout.preferredHeight: iconSize
Layout.preferredWidth: iconSize
Layout.leftMargin: 5
Layout.rightMargin: 15
Layout.alignment: Qt.AlignVCenter
isCustom: true
backgroundRadius: width
colorSet: (videoItem.muted ? FileViewDialogStyle.speakerOff
: FileViewDialogStyle.speakerOn)
onClicked: videoItem.muted = !videoItem.muted
}
]
onSeekRequested: {
videoItem.seek(ms)
}
}
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
// ANIMATIONS
//--------------------------------------------------------------------------------------------------
Component {
id: animatedImageComponent
AnimatedImage {
id: animatedImageSource
property real scaleAnimatorTo : 1.7
mipmap: SettingsModel.mipmapEnabled
source: 'file:/'+mainItem.filePath
autoTransform: true
fillMode: Image.PreserveAspectFit
}
}
//--------------------------------------------------------------------------------------------------
// IMAGE
//--------------------------------------------------------------------------------------------------
Component {
id: imageComponent
Image {
id: imageSource
mipmap: SettingsModel.mipmapEnabled
source: 'file:/'+mainItem.filePath
autoTransform: true
fillMode: Image.PreserveAspectFit
}
}
//--------------------------------------------------------------------------------------------------
// PLAIN TEXT
//--------------------------------------------------------------------------------------------------
Component{
id: fileTextComponent
ListView {
id: idContentListView
model: idContentListView.stringList
anchors.fill: parent
anchors.topMargin: 20
anchors.leftMargin: 10
anchors.rightMargin: 10
clip: true
delegate: Text {
width: idContentListView.width
text: model.modelData
font.pointSize: FormTableStyle.entry.text.pointSize
textFormat: Text.PlainText
wrapMode: Text.Wrap
}
ScrollBar.vertical: ScrollBar {}
property variant stringList: null
function updateText() {
stringList = UtilsCpp.getFileContent(filePath).split('\n')
//idContentListView.positionViewAtEnd()
}
Component.onCompleted: updateText()
}
}
//--------------------------------------------------------------------------------------------------
// NO DISPLAY
//--------------------------------------------------------------------------------------------------
Component {
id: placeholderComponent
Icon{
id: fileIcon
Layout.alignment: Qt.AlignCenter
icon: FileViewDialogStyle.extension.unknownIcon
iconSize: FileViewDialogStyle.extension.iconSize
}
}
}
}

View file

@ -45,7 +45,6 @@ Item {
return files
}, [])
if (files.length > 0) {
dropped(files)
}

View file

@ -0,0 +1,43 @@
import QtQuick 2.7
import Common 1.0
import Utils 1.0
import UtilsCpp 1.0
MouseArea{
id: mainItem
property bool realRunning : false
property bool firstUse: true
Timer {
id: hideButtonsTimer
interval: mainItem.firstUse ? 500 : 4000
running: false
triggeredOnStart: !mainItem.firstUse
onTriggered: {if(!mainItem.firstUse && mainItem.realRunning != running) mainItem.realRunning = running
mainItem.firstUse = false}
function startTimer(){
if(!mainItem.firstUse || !running)
restart();
}
function stopTimer(){
stop()
mainItem.realRunning = false
mainItem.firstUse = false
}
}
acceptedButtons: Qt.NoButton
propagateComposedEvents: true
cursorShape: undefined
//cursorShape: Qt.ArrowCursor
onEntered: hideButtonsTimer.startTimer()
onExited: {
var cursorPosition = UtilsCpp.getCursorPosition()
mapToItem(window.contentItem, cursorPosition.x, cursorPosition.y)
if (cursorPosition.x <= 0 || cursorPosition.y <= 0
|| cursorPosition.x >= width || cursorPosition.y >= height)
hideButtonsTimer.stopTimer()
}
onPositionChanged: hideButtonsTimer.startTimer()
}

View file

@ -14,6 +14,7 @@ import Common.Styles 1.0
ProgressBar {
id: progressBar
property alias customActions: customActions.data
property bool stopAtEnd: true
property bool resetAtEnd: false
property int progressDuration // Max duration
@ -21,7 +22,10 @@ ProgressBar {
property alias colorSet: progression.colorSet
property alias backgroundColor: backgroundArea.color
property alias durationTextColor: durationText.color
property alias progressLineBackgroundColor: progressionLineBackground.color
property int waveLeftMargin: 0
property int progressSize: height
property bool blockValueAtEnd: true
function start(){
progressBar.value = 0
@ -45,7 +49,7 @@ ProgressBar {
interval: 5
}
to: 101
value: 0
value: progressPosition * to / progressDuration
onValueChanged:{
if(value > 100){
if( progressBar.stopAtEnd)
@ -53,7 +57,7 @@ ProgressBar {
if(progressBar.resetAtEnd) {
progressBar.value = 0
progressPosition = 0
}else{
}else if(progressBar.blockValueAtEnd){
progressBar.value = 100// Stay at 100
progressPosition = progressDuration
}
@ -82,19 +86,29 @@ ProgressBar {
RowLayout{
anchors.fill: parent
spacing: 0
ActionButton{
id: progression
RowLayout {
id: customActions
visible: children.length>0
}
Rectangle{
id: progressionLineBackground
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: progressBar.waveLeftMargin
backgroundRadius: 5
fillMode: Image.TileHorizontally
verticalAlignment: Image.AlignLeft
horizontalAlignment: Image.AlignLeft
isCustom: true
colorSet: MediaProgressBarStyle.progressionWave
percentageDisplayed: 0
onClicked: progressBar.seekRequested(x * progressBar.progressDuration/width)
Layout.preferredHeight: progressBar.progressSize
color: 'transparent'
radius: 5
ActionButton{
id: progression
anchors.fill: parent
backgroundRadius: 5
fillMode: Image.TileHorizontally
verticalAlignment: Image.AlignLeft
horizontalAlignment: Image.AlignLeft
isCustom: true
colorSet: MediaProgressBarStyle.progressionWave
percentageDisplayed: 0
onClicked: progressBar.seekRequested(x * progressBar.progressDuration/width)
}
}
Text{
id: durationText

View file

@ -0,0 +1,109 @@
pragma Singleton
import QtQml 2.2
import Units 1.0
import ColorsList 1.0
// =============================================================================
QtObject {
property string sectionName : 'FileView'
property QtObject progression: QtObject{
property int iconSize: 60
property int iconHeight: 60
property int iconWidth: 60
property string name : 'progression'
property string icon : ''
property color backgroundColor: ColorsList.addImageColor(sectionName+'_'+name+'_bg', icon, 'c80').color
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'w_n_b_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'w_h_b_bg').color
property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'w_p_b_bg').color
property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'w_n_b_fg').color
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'w_h_b_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'w_p_b_fg').color
property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'l_n_b_bg').color
property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'l_h_b_bg').color
property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'l_p_b_bg').color
property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'l_n_b_fg').color
property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'l_h_b_fg').color
property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'l_p_b_fg').color
}
property QtObject pauseAction: QtObject {
property int iconSize: 25
property string name : 'pause'
property string icon : 'chat_audio_pause_custom'
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color
property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color
property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color
property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color
property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color
}
property QtObject playAction: QtObject {
property int iconSize: 25
property string name : 'play'
property string icon : 'chat_audio_play_custom'
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color
property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color
property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color
property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color
property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color
}
property QtObject speakerOn: QtObject {
property int iconSize: 25
property string icon : 'speaker_on_custom'
property string name : 'speakerOn'
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color
property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color
property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color
property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color
property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color
}
property QtObject speakerOff: QtObject {
property int iconSize: 25
property string icon : 'speaker_off_custom'
property string name : 'speakerOff'
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color
property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color
property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color
property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color
property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color
}
property QtObject exportFile: QtObject{
property string icon: 'download_custom'
property string name : 'exportFile'
property int height: 20
property int pointSize: Units.dp * 8
property int iconSize: 25
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color
property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color
property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color
property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color
property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color
}
property QtObject extension: QtObject {
property string unknownIcon: 'file_unknown_custom'
property int iconSize: 60
}
}

View file

@ -8,6 +8,7 @@ singleton BusyIndicatorStyle 1.0 Animations/BusyIndicatorStyle.qml
singleton DialogStyle 1.0 Dialog/DialogStyle.qml
singleton DateTimeDialogStyle 1.0 Dialog/DateTimeDialogStyle.qml
singleton FileViewDialogStyle 1.0 Dialog/FileViewDialogStyle.qml
singleton ActionBarStyle 1.0 Form/ActionBarStyle.qml
singleton ActionSwitchStyle 1.0 Form/ActionSwitchStyle.qml

View file

@ -16,6 +16,8 @@ DateTimeDialog 1.0 Dialog/DateTimeDialog.qml
DialogDescription 1.0 Dialog/DialogDescription.qml
ConfirmDialog 1.0 Dialog/ConfirmDialog.qml
DialogPlus 1.0 Dialog/DialogPlus.qml
FileViewDialog 1.0 Dialog/FileViewDialog.qml
ActionBar 1.0 Form/ActionBar.qml
ActionButton 1.0 Form/ActionButton.qml
@ -65,6 +67,7 @@ TabButton 1.0 Form/Tab/TabButton.qml
TabContainer 1.0 Form/Tab/TabContainer.qml
DragBox 1.0 Helpers/DragBox.qml
HoveringMouseArea 1.0 Helpers/HoveringMouseArea.qml
InvertedMouseArea 1.0 Helpers/InvertedMouseArea.qml
Icon 1.0 Image/Icon.qml

View file

@ -32,8 +32,18 @@ Loader{
property bool isOutgoing : contentModel && contentModel.chatMessageModel && (contentModel.chatMessageModel.isOutgoing || contentModel.chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle);
property bool isActive: active
property string filePath : tempFile.filePath
active: contentModel && contentModel.isVoiceRecording()
onContentModelChanged: if(contentModel){
tempFile.createFileFromContent(contentModel, false);
}
TemporaryFile {
id: tempFile
}
sourceComponent: Item{
id: loadedItem
property bool isPlaying : vocalPlayer.item && vocalPlayer.item.playbackState === SoundPlayer.PlayingState
@ -59,7 +69,7 @@ Loader{
}
}
sourceComponent: SoundPlayer {
source: mainItem.contentModel && mainItem.contentModel.filePath
source: mainItem.contentModel && mainItem.filePath
onStopped:{
mediaProgressBar.value = 101
}

View file

@ -280,14 +280,21 @@ Row {
onClicked: {
if(rectangle.isTransferring)
mainRow.contentModel.cancelDownloadFile()
else if (Utils.pointIsInItem(this, thumbnailProvider, mouse)) {
mainRow.contentModel.openFile()
} else if (mainRow.contentModel && mainRow.contentModel.wasDownloaded) {
mainRow.contentModel.openFile(true)// Show directory
else if( !mainRow.contentModel.wasDownloaded) {
thumbnailProvider.state = ''
} else {
mainRow.contentModel.downloadFile()
}else if (Utils.pointIsInItem(this, thumbnailProvider, mouse)) {
window.attachVirtualWindow(Utils.buildCommonDialogUri('FileViewDialog'), {
contentModel: mainRow.contentModel,
}, function (status) {
})
} else if (mainRow.contentModel ) {
thumbnailProvider.state = ''
mainRow.contentModel.openFile(true)// Show directory
} else {
thumbnailProvider.state = ''
mainRow.contentModel.downloadFile()
}
}
onExited: thumbnailProvider.state = ''

View file

@ -12,7 +12,7 @@ QtObject {
property int emptySpace: 10
property color color: ColorsList.add(sectionName, 'q').color
property color backgroundColor: ColorsList.add(sectionName+'_bg', 'a').color
property color backgroundColor: ColorsList.add(sectionName+'_bg', 'e').color
property QtObject pauseAction: QtObject {
property int iconSize: 25

View file

@ -591,41 +591,9 @@ Window {
visible: SettingsModel.showTelKeypadAutomatically
y: 50
}
MouseArea{
Timer {
id: hideButtonsTimer
property bool realRunning : false
property bool firstUse: true
interval: firstUse ? 500 : 4000
running: false
triggeredOnStart: !firstUse
onTriggered: {if(!firstUse && realRunning != running) realRunning = running
firstUse = false}
function startTimer(){
if(!firstUse || !running)
restart();
}
function stopTimer(){
stop()
realRunning = false
hideButtonsTimer.firstUse = false
}
}
HoveringMouseArea{
id: hideButtonsTimer
anchors.fill: parent
acceptedButtons: Qt.NoButton
propagateComposedEvents: true
cursorShape: undefined
//cursorShape: Qt.ArrowCursor
onEntered: hideButtonsTimer.startTimer()
onExited: {
var cursorPosition = UtilsCpp.getCursorPosition()
mapToItem(window.contentItem, cursorPosition.x, cursorPosition.y)
if (cursorPosition.x <= 0 || cursorPosition.y <= 0
|| cursorPosition.x >= width || cursorPosition.y >= height)
hideButtonsTimer.stopTimer()
}
onPositionChanged: hideButtonsTimer.startTimer()
}
}

View file

@ -4,6 +4,7 @@ import QtQuick.Controls 2.5
import Common 1.0
import Linphone 1.0
import Utils 1.0
import App.Styles 1.0
import Linphone.Styles 1.0
@ -327,6 +328,56 @@ TabContainer {
}
}
// -------------------------------------------------------------------------
// VFS
// -------------------------------------------------------------------------
Form {
//: 'VFS'
title: qsTr('vfsTitle')
width: parent.width
FormLine {
FormGroup {
//: 'Encrypt all the application' : Label to encrypt application
label: qsTr('vfsEncryption')
Switch {
checked: SettingsModel.isVfsEncrypted
onClicked: {
window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), {
descriptionText: (checked
//: 'Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.' : Explanation to deactivate the VFS encryption.
? qsTr('vfsDeactivation')
//: 'Are you sure to activate the encryption? You cannot revert without deleting ALL your data' : Explanation to activate the VFS encryption.
: qsTr('vfsActivation'))
, buttonTexts : (checked
?[qsTr('cancel'),
//: 'Delete data' : Action to delete all data.
qsTr('deleteData')]
: [qsTr('cancel'), qsTr('confirm')])
, height:320
}, function (status) {
if(status > 0){
if (status == 1 && checked){// Test on 1 in case if we want more options (aka more buttons)
window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), {
//: 'The application will delete your application data files. Do you confirm ?'
descriptionText: qsTr('vfsDeletion')
, height: 320
}, function (deleteStatus) {
if( deleteStatus)
SettingsModel.setVfsEncrypted(!checked, true)
})
}else
SettingsModel.setVfsEncrypted(!checked, false)
}
})
}
}
}
}
}
// -------------------------------------------------------------------------
// Developer settings.
// -------------------------------------------------------------------------