diff --git a/CMakeGraphVizOptions.cmake b/CMakeGraphVizOptions.cmake index 26b3ceb..acd5748 100644 --- a/CMakeGraphVizOptions.cmake +++ b/CMakeGraphVizOptions.cmake @@ -4,7 +4,7 @@ # cmake --build . --target architecture ######################################## -SET(GRAPHVIZ_IGNORE_TARGETS AusweisAppGlobal;AusweisAppExternal;AusweisAppUiCli;cvc;fuzz;OpenSsl;tlscheck;Test;Script) +SET(GRAPHVIZ_IGNORE_TARGETS AusweisAppGlobal;AusweisAppExternal;cvc;fuzz;OpenSsl;tlscheck;Test;Script) SET(GRAPHVIZ_EXTERNAL_LIBS OFF) SET(GRAPHVIZ_EXECUTABLES ON) SET(GRAPHVIZ_GENERATE_PER_TARGET OFF) diff --git a/CMakeLists.txt b/CMakeLists.txt index 581cccc..141bb3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.5.0) +CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0) IF(POLICY CMP0020) CMAKE_POLICY(SET CMP0020 NEW) @@ -37,7 +37,7 @@ ELSE() ENDIF() -PROJECT(AusweisApp2 VERSION 1.16.2 LANGUAGES ${LANGUAGES}) +PROJECT(AusweisApp2 VERSION 1.18.0 LANGUAGES ${LANGUAGES}) # Set TWEAK if not defined in PROJECT_VERSION above to # have a valid tweak version without propagating it @@ -49,7 +49,8 @@ ENDIF() IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) SET(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/dist" CACHE PATH "default install path" FORCE) ENDIF() -SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") +SET(CMAKE_DIR "${PROJECT_SOURCE_DIR}/cmake") +SET(CMAKE_MODULE_PATH "${CMAKE_DIR}") OPTION(BUILD_SHARED_LIBS "Enable build of shared libraries") INCLUDE(Helper) @@ -133,7 +134,7 @@ ADD_SUBDIRECTORY(src) IF("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") INCLUDE(CTest) - CONFIGURE_FILE("${CMAKE_MODULE_PATH}/CTestCustom.cmake.in" "${CMAKE_BINARY_DIR}/CTestCustom.cmake" @ONLY) + CONFIGURE_FILE("${CMAKE_DIR}/CTestCustom.cmake.in" "${CMAKE_BINARY_DIR}/CTestCustom.cmake" @ONLY) CONFIGURE_FILE("${RESOURCES_DIR}/sonar-project.properties.in" "${CMAKE_BINARY_DIR}/sonar-project.properties" @ONLY) ENDIF() diff --git a/LICENSE.officially.txt b/LICENSE.officially.txt index fad0d6c..39ca56b 100644 --- a/LICENSE.officially.txt +++ b/LICENSE.officially.txt @@ -1,50 +1,7 @@ Nutzungsbedingungen -Allgemeines zur Installation +Bitte lesen Sie zunächst die Lizenzbestimmungen und deren Ergänzungen für die Überlassung und Nutzung der AusweisApp2 durch. Nachdem Sie diesen zugestimmt haben, können Sie die Installation fortsetzen. Datenschutzrechtliche Hinweise sind unter https://www.ausweisapp.bund.de/datenschutz/ abrufbar. -Bitte lesen Sie zunächst die Allgemeinen Geschäftsbedingungen so wie die Lizenzbedingungen für die Überlassung und Nutzung der AusweisApp2 durch. Nachdem Sie zugestimmt haben, können Sie die Installation fortsetzen. - - - Allgemeine Geschäftsbedingungen - -§ 1 Nutzungsbedingungen und Datenschutzhinweis -(1) Diese Allgemeine Geschäftsbedingungen (AGB) des Bundes, vertreten durch das Bundesministerium des Innern, für Bau und Heimat (nachfolgend "Bund") und dem Nutzer gelten für die Überlassung und Nutzung der Software AusweisApp2 (nachfolgend AusweisApp2) und deren neue Versionen, die auf der Grundlage dieser Bedingungen überlassen werden. -(2) "Nutzer" im Sinne dieses Vertrages sind natürliche Personen. - -§ 2 Vertragsgegenstand und Unentgeltlichkeit -(1) Die AusweisApp2 wird regelmäßig hinsichtlich ihrer Konformität zu den Technischen Richtlinien des Bundesamtes für Sicherheit in der Informationstechnik [TR-03124-2] zertifiziert sowie hinsichtlich ihrer Nutzerfreundlichkeit [EN ISO 9241 Teil 110 Ergonomie Mensch-System-Integration zur Prüfung der Benutzbarkeit und Funktionalität und Trusted Design Guidelines zur Prüfung der Vertrauenswürdigkeit] und auf Barrierefreiheit [BITV 2.0] überprüft. -(2) Die AusweisApp2 wird in maschinenlesbarer Form unentgeltlich überlassen. Der Quellcode der AusweisApp2 wird ebenfalls unentgeltlich veröffentlicht. Eine Auflistung der verwendeten Open Source-Bestandteile der AusweisApp2 findet sich im Anhang. -(3) Die AusweisApp2 wird von der Governikus GmbH & Co. KG (Governikus) als Erfüllungsgehilfe des Bundes überlassen. - -§ 3 Verwendungszweck -Die AusweisApp2 und alle ihre Bestandteile dienen ausschließlich dazu, im Zusammenhang mit der Nutzung der Online-Ausweisfunktion des Personalausweises bzw. des elektronischen Aufenthaltstitels und der Übermittlung der damit verbundenen Daten gemäß den gesetzlichen Vorgaben verwendet zu werden. - -§ 4 Pflichten und Obliegenheiten des Nutzers -(1) Der Nutzer verpflichtet sich, die AusweisApp2 entsprechend des in § 3 genannten Verwendungszweckes zu nutzen. Es liegt in seinem Interesse, dass in Verbindung mit der AusweisApp2 genutzte Hard- und Software des Nutzers immer auf dem neuesten Stand der Sicherheitstechnik (System- und Firmware Update, Virenscanner, Firewall usw.) sind. -(2) Es liegt im eigenen Interesse des Nutzers aber auch des Bundes, dass stets nur die neueste Version der AusweisApp2 (siehe § 5) verwendet wird. - -§ 5 Pflege und Support -(1) Der Bund bietet nach eigenem Ermessen und ohne hierzu verpflichtet zu sein für Teile der AusweisApp2 zusätzliche kostenfreie Supportleistungen in Form von Dokumentationen und online Hilfen auf dem AusweisApp2-Portal im Internet unter der Adresse www.ausweisapp.bund.de an, sowie über die Hotline des Herstellers Governikus unter der E-Mail-Adresse: support@ausweisapp.de und der Tel.-Nr.: +49 421 204 95-995. Auch stellt er verfügbare neue Versionen der AusweisApp2 zur Verfügung. Hieraus erwächst jedoch kein zusätzlicher Anspruch auf Mängelbeseitigung, auf Zertifizierung, auf Beibehaltung der Supportleistungen oder der Hotline und auf Überlassung neuer Versionen. -(2) Verfügbare neue Versionen der AusweisApp2 können im Internet kostenfrei auf dem AusweisApp2-Portal unter der Adresse www.ausweisapp.bund.de sowie über allgemein zugängliche AppStore heruntergeladen werden. -(3) Eventuelle Mängel der AusweisApp2 werden grundsätzlich dadurch behoben, dass der Bund jeweils eine neue Version der AusweisApp2 zum Herunterladen zur Verfügung stellt (siehe § 5 Absatz 1). Eine Pflicht zur Bereitstellung von neuen Versionen ergibt sich daraus grundsätzlich nicht. - -§ 6 Hinweis auf gewerbliche und urheberrechtliche Schutzrechte -Die Zeichen AusweisApp2 und die entsprechenden Grafiken sowie das Signet zur Online-Ausweisfunktion sind für den Bund als Marken geschützt. - -§ 7 Datenschutzhinweis -(1) Mit dieser Software werden personenbezogene Daten im Sinne des § 3 Absatz 1 des Bundesdatenschutzgesetzes (BDSG) zum Zwecke der Verarbeitung grundsätzlich nicht erhoben. -(2) Personenbezogene Daten aus dem Ausweischip werden ausschließlich zum Zweck des elektronischen Identitätsnachweises im Umfang der erteilten Berechtigung nach Einwilligung des Nutzers mit seiner Ausweis-PIN-Eingabe und auf dem Transportweg zwischen dem Ausweischip und dem Diensteanbieter sicher verschlüsselt und auch für Governikus nicht lesbar übertragen. -(3) Neben den reinen Daten, die zur Identifizierung/ Authentisierung benötigt werden, erhebt die Software nur mit Einverständnis des Nutzers Daten über die Art und Version der Anwendungsumgebung (Betriebssystem, Lesegeräte usw.) des Nutzers. Diese Daten kann der Nutzer bei Bedarf an Governikus übermitteln, um die ordnungsgemäße Verarbeitung prüfen zu lassen und Fehlern im Verarbeitungsprozess vorzubeugen. Die Nutzung der Daten erfolgt dann im Rahmen des § 14 BDSG durch Governikus im Auftrag des Bundes und nur für den genannten Zweck. -(4) Die in der AusweisApp2 enthaltene Selbstauskunft über die im Chip gespeicherten Daten, ist ein Dienst der durch Governikus über das Internet erbracht wird. Die Ausweisdaten werden dabei nur zur Anzeige des Nutzers gebracht und nicht dauerhaft bei Governikus gespeichert oder für andere Zwecke verwendet. - -§ 8 Deutsches Recht -Auf diese Nutzungsbedingungen ist ausschließlich deutsches Recht unter Ausschluss des Übereinkommens der Vereinten Nationen über Verträge über den internationalen Warenkauf (CISG) anwendbar. - - -Anhang - -Eingearbeitete Open Source-Komponenten -Qt, OpenSSL und http_parser. @@ -344,6 +301,68 @@ Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: + Lizenzergänzungen + +§ 1 Nutzungsbedingungen +(1) Diese Allgemeine Geschäftsbedingungen (AGB) des Bundes, vertreten durch das Bundesamt für Sicherheit in der Informationstechnik (nachfolgend "Bund") und dem Nutzer gelten für die Überlassung und Nutzung der Software AusweisApp2 (nachfolgend AusweisApp2) und deren neue Versionen, die auf der Grundlage dieser Bedingungen überlassen werden. +(2) "Nutzer" im Sinne dieses Vertrages sind natürliche Personen. + +§ 2 Vertragsgegenstand und Unentgeltlichkeit +(1) Die AusweisApp2 wird regelmäßig hinsichtlich ihrer Konformität zu den Technischen Richtlinien des Bundesamtes für Sicherheit in der Informationstechnik [TR-03124-2] zertifiziert sowie hinsichtlich ihrer Nutzerfreundlichkeit [EN ISO 9241 Teil 110 Ergonomie Mensch-System-Integration zur Prüfung der Benutzbarkeit und Funktionalität und Trusted Design Guidelines zur Prüfung der Vertrauenswürdigkeit] und auf Barrierefreiheit [BITV 2.0] überprüft. +(2) Die AusweisApp2 wird in maschinenlesbarer Form unentgeltlich überlassen. Der Quellcode der AusweisApp2 wird ebenfalls unentgeltlich veröffentlicht. Eine Auflistung der verwendeten Open Source-Bestandteile der AusweisApp2 findet sich im Anhang. +(3) Die AusweisApp2 wird von der Governikus GmbH & Co. KG (Governikus) als Erfüllungsgehilfe des Bundes überlassen. + +§ 3 Verwendungszweck +Die AusweisApp2 und alle ihre Bestandteile dienen ausschließlich dazu, im Zusammenhang mit der Nutzung der Online-Ausweisfunktion des Personalausweises bzw. des elektronischen Aufenthaltstitels und der Übermittlung der damit verbundenen Daten gemäß den gesetzlichen Vorgaben verwendet zu werden. + +§ 4 Pflichten und Obliegenheiten des Nutzers +(1) Der Nutzer verpflichtet sich, die AusweisApp2 entsprechend des in § 3 genannten Verwendungszweckes zu nutzen. Es liegt in seinem Interesse, dass in Verbindung mit der AusweisApp2 genutzte Hard- und Software des Nutzers immer auf dem neuesten Stand der Sicherheitstechnik (System- und Firmware Update, Virenscanner, Firewall usw.) sind. +(2) Es liegt im eigenen Interesse des Nutzers aber auch des Bundes, dass stets nur die neueste Version der AusweisApp2 (siehe § 5) verwendet wird. + +§ 5 Pflege und Support +(1) Der Bund bietet nach eigenem Ermessen und ohne hierzu verpflichtet zu sein für Teile der AusweisApp2 zusätzliche kostenfreie Supportleistungen in Form von Dokumentationen und online Hilfen auf dem AusweisApp2-Portal im Internet unter der Adresse www.ausweisapp.bund.de an, sowie über die Hotline des Herstellers Governikus unter der E-Mail-Adresse: support@ausweisapp.de und der Tel.-Nr.: +49 421 204 95-995. Auch stellt er verfügbare neue Versionen der AusweisApp2 zur Verfügung. Hieraus erwächst jedoch kein Anspruch auf Mängelbeseitigung, auf Zertifizierung, auf Beibehaltung der Supportleistungen oder der Hotline und auf Überlassung neuer Versionen. +(2) Verfügbare neue Versionen der AusweisApp2 können im Internet kostenfrei auf dem AusweisApp2-Portal unter der Adresse www.ausweisapp.bund.de sowie über allgemein zugängliche AppStore heruntergeladen werden. +(3) Eventuelle Mängel der AusweisApp2 werden grundsätzlich dadurch behoben, dass der Bund jeweils eine neue Version der AusweisApp2 zum Herunterladen zur Verfügung stellt (siehe § 5 Absatz 1). Eine Pflicht zur Bereitstellung von neuen Versionen ergibt sich daraus nicht. + +§ 6 Hinweis auf gewerbliche und urheberrechtliche Schutzrechte +Die Zeichen AusweisApp2 und die entsprechenden Grafiken sowie das Signet zur Online-Ausweisfunktion sind für den Bund als Marken geschützt. + +§ 7 Deutsches Recht +Auf diese Nutzungsbedingungen ist ausschließlich deutsches Recht unter Ausschluss des Übereinkommens der Vereinten Nationen über Verträge über den internationalen Warenkauf (CISG) anwendbar. + + +Anhang + + Ergänzende Lizenzhinweise + +Die verwendeten Open-Source-Bibliotheken unterliegen den folgenden Nutzungsbedingungen: + +OpenSSL + Lizenz: OpenSSL license & SSLeay license + Version: 1.1.1c + Adresse: https://www.openssl.org/ + +Qt + Lizenz: LGPL v3 + Version: 5.12.4 + Adresse: https://www.qt.io/ + +http_parser + Lizenz: MIT + Version: 2.9.2 + Adresse: https://github.com/nodejs/http-parser/ + +Android Support Library V4 + Lizenz: Apache 2.0 + Version: 21.0.3 + Adresse: https://developer.android.com/topic/libraries/support-library/ + + +Die Lizenztexte lauten in ihrer originalen Fassung wie folgt: + + + + Copyright (c) 1998-2017 The OpenSSL Project. All rights reserved. @@ -459,33 +478,6 @@ copied and put under another distribution licence - - Ergänzende Linzenzhinweise - -Die verwendeten OpenSource-Bibliotheken unterliegen den folgenden Nutzungsbedingungen: - -Qt - Lizenz: LGPL v3 - Version: 5.11.2 - Adresse: https://www.qt.io/ - -http_parser - Lizenz: MIT - Version: 2.8.1 - Adresse: https://github.com/nodejs/http-parser/ - -Android Support Library V4 - Lizenz: Apache 2.0 - Version: 21.0.3 - Adresse: https://developer.android.com/topic/libraries/support-library/ - - -Die Lizenztexte lauten in ihrer originalen Fassung wie folgt: - - - - - GNU LESSER GENERAL PUBLIC LICENSE The Qt Toolkit is Copyright (C) 2016 The Qt Company Ltd. @@ -867,28 +859,3 @@ IN THE SOFTWARE. of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/LICENSE.txt b/LICENSE.txt index 13f65b5..a1de861 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -294,6 +294,38 @@ Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: +Anhang + + Ergänzende Lizenzhinweise + +Die verwendeten Open-Source-Bibliotheken unterliegen den folgenden Nutzungsbedingungen: + +OpenSSL + Lizenz: OpenSSL license & SSLeay license + Version: 1.1.1c + Adresse: https://www.openssl.org/ + +Qt + Lizenz: LGPL v3 + Version: 5.12.4 + Adresse: https://www.qt.io/ + +http_parser + Lizenz: MIT + Version: 2.9.2 + Adresse: https://github.com/nodejs/http-parser/ + +Android Support Library V4 + Lizenz: Apache 2.0 + Version: 21.0.3 + Adresse: https://developer.android.com/topic/libraries/support-library/ + + +Die Lizenztexte lauten in ihrer originalen Fassung wie folgt: + + + + Copyright (c) 1998-2017 The OpenSSL Project. All rights reserved. @@ -409,33 +441,6 @@ copied and put under another distribution licence - - Ergänzende Linzenzhinweise - -Die verwendeten OpenSource-Bibliotheken unterliegen den folgenden Nutzungsbedingungen: - -Qt - Lizenz: LGPL v3 - Version: 5.11.2 - Adresse: https://www.qt.io/ - -http_parser - Lizenz: MIT - Version: 2.8.1 - Adresse: https://github.com/nodejs/http-parser/ - -Android Support Library V4 - Lizenz: Apache 2.0 - Version: 21.0.3 - Adresse: https://developer.android.com/topic/libraries/support-library/ - - -Die Lizenztexte lauten in ihrer originalen Fassung wie folgt: - - - - - GNU LESSER GENERAL PUBLIC LICENSE The Qt Toolkit is Copyright (C) 2016 The Qt Company Ltd. @@ -817,28 +822,3 @@ IN THE SOFTWARE. of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.rst b/README.rst index b5fe6d6..69b18bd 100644 --- a/README.rst +++ b/README.rst @@ -134,7 +134,7 @@ Für iOS wird die AusweisApp2 mittels XCode gebaut! $ cd build $ cmake -DCMAKE_PREFIX_PATH=/Users/governikus/Toolchain/dist -DCMAKE_TOOLCHAIN_FILE=../AusweisApp2/cmake/iOS.toolchain.cmake -DCMAKE_BUILD_TYPE=release ../AusweisApp2 -GXcode - $ xcodebuild -target install -configuration Release + $ xcodebuild -configuration Release $ xcodebuild -target ipa -configuration Release diff --git a/appveyor.yml b/appveyor.yml index d1a1bfa..04ac20e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,28 +3,21 @@ version: "{build}" environment: matrix: - PlatformToolset: mingw-w64 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - QTPath: C:\Qt\5.11\mingw53_32 - OPENSSLPath: C:\OpenSSL-v111-Win32 - - - PlatformToolset: v140 - platform: x64 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - QTPath: C:\Qt\5.11\msvc2015_64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + QTPath: C:\Qt\5.12\mingw73_64 OPENSSLPath: C:\OpenSSL-v111-Win64 - ARCHI: amd64 - - PlatformToolset: v140 + - PlatformToolset: v141 platform: Win32 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - QTPath: C:\Qt\5.11\msvc2015 + QTPath: C:\Qt\5.12\msvc2017 OPENSSLPath: C:\OpenSSL-v111-Win32 ARCHI: x86 - PlatformToolset: v141 platform: x64 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - QTPath: C:\Qt\5.11\msvc2017_64 + QTPath: C:\Qt\5.12\msvc2017_64 OPENSSLPath: C:\OpenSSL-v111-Win64 ARCHI: amd64 @@ -33,8 +26,7 @@ configuration: #- Debug install: - - if "%PlatformToolset%"=="mingw-w64" set PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH:C:\Program Files\Git\usr\bin;=% - - if "%PlatformToolset%"=="v140" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %archi% + - if "%PlatformToolset%"=="mingw-w64" set PATH=C:\mingw-w64\x86_64-7.3.0-posix-seh-rt_v5-rev0\mingw64\bin;%PATH:C:\Program Files\Git\usr\bin;=% - if "%PlatformToolset%"=="v141" call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" %archi% build: @@ -47,7 +39,6 @@ before_build: $generator = switch ($env:PLATFORMTOOLSET) { "v141" {"Visual Studio 15 2017"} - "v140" {"Visual Studio 14 2015"} "mingw-w64" {"MinGW Makefiles"} } if ($env:PLATFORM -eq "x64") diff --git a/cmake/Appcast.cmake b/cmake/Appcast.cmake index e2f4b42..50547b5 100644 --- a/cmake/Appcast.cmake +++ b/cmake/Appcast.cmake @@ -10,7 +10,11 @@ IF(MAC OR LINUX OR WIN32) STRING(TIMESTAMP APPCAST_DATE "%Y-%m-%dT%H:%M:%S") FOREACH(filePath ${_files}) - FILE_SIZE(fileSize ${filePath}) + IF(CMAKE_VERSION VERSION_LESS "3.14") + FILE_SIZE(fileSize ${filePath}) + ELSE() + FILE(SIZE ${filePath} fileSize) + ENDIF() GET_FILENAME_COMPONENT(file ${filePath} NAME) IF(NOT DEFINED fileSize) diff --git a/cmake/CompilerFlags.cmake b/cmake/CompilerFlags.cmake index bfab901..cfad5f6 100644 --- a/cmake/CompilerFlags.cmake +++ b/cmake/CompilerFlags.cmake @@ -6,6 +6,10 @@ ADD_DEFINITIONS(-DQT_NO_FOREACH) ADD_DEFINITIONS(-DQT_NO_KEYWORDS) ADD_DEFINITIONS(-DQT_NO_EXCEPTIONS) +IF(NOT MSVC AND NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + ADD_DEFINITIONS(-DQT_STRICT_ITERATORS) +ENDIF() + IF(CMAKE_VERSION VERSION_LESS "3.12") ADD_DEFINITIONS(-DQT_RESTRICTED_CAST_FROM_ASCII) ELSE() @@ -18,7 +22,7 @@ IF(QT_VENDOR STREQUAL "Governikus") ADD_DEFINITIONS(-DQT_DEPRECATED_WARNINGS) ENDIF() -SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_CXX_STANDARD_REQUIRED ON) SET(CMAKE_CXX_EXTENSIONS OFF) @@ -34,22 +38,23 @@ IF(MSVC) ENDIF() ADD_FLAG(/Qspectre) ELSE() - ADD_DEFINITIONS(-DQT_STRICT_ITERATORS) - STRING(REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") STRING(REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wcast-qual -Wshadow") SET(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -g") IF(ANDROID AND CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") - SET(USE_LD bfd) + SET(PREFER_LD bfd CACHE STRING "") ELSE() - SET(USE_LD gold) + SET(PREFER_LD gold CACHE STRING "") ENDIF() - ADD_FLAG(-fuse-ld=${USE_LD} VAR CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS LINK -fuse-ld=${USE_LD}) - ADD_FLAG(-flto VAR CMAKE_EXE_LINKER_FLAGS_RELEASE CMAKE_SHARED_LINKER_FLAGS_RELEASE LINK -flto) - ADD_FLAG(-fno-rtti VAR CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL) + ADD_FLAG(-fuse-ld=${PREFER_LD} VAR CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS USE_SAME_FOR_LINKER USE_LINKER_ONLY) + ADD_FLAG(-flto VAR CMAKE_EXE_LINKER_FLAGS_RELEASE CMAKE_SHARED_LINKER_FLAGS_RELEASE USE_SAME_FOR_LINKER) + + IF(NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + ADD_FLAG(-fno-rtti VAR CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL) + ENDIF() ADD_FLAG(-fno-exceptions) ADD_FLAG(-fstack-protector-strong -fstack-protector) @@ -74,6 +79,8 @@ ELSE() ADD_FLAG(-Wunreachable-code-aggressive) ADD_FLAG(-Wnewline-eof) ADD_FLAG(-Wdate-time) + ADD_FLAG(-Wunused) + ADD_FLAG(-Wunused-template) ADD_FLAG(-Wno-gnu-zero-variadic-macro-arguments) # Qt (qDebug) is not compatible @@ -119,7 +126,7 @@ ELSE() ENDIF() IF(APPLE AND NOT IOS) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ObjC++ -mmacosx-version-min=10.11") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ObjC++ -mmacosx-version-min=10.12") ENDIF() @@ -131,5 +138,5 @@ INCLUDE(CompilerFlagsPersoSim) OPTION(COVERAGE "Enable code coverage") IF(COVERAGE) MESSAGE(STATUS "Enable code coverage instrumentation") - ADD_FLAG(--coverage LINK --coverage VAR CMAKE_CXX_FLAGS_DEBUG) + ADD_FLAG(--coverage VAR CMAKE_CXX_FLAGS_DEBUG USE_SAME_FOR_LINKER) ENDIF() diff --git a/cmake/DefaultFiles.cmake b/cmake/DefaultFiles.cmake index 03538c0..df01749 100644 --- a/cmake/DefaultFiles.cmake +++ b/cmake/DefaultFiles.cmake @@ -21,7 +21,4 @@ FUNCTION(CONFIGURE_DEFAULT_FILES _destination) # Copy secure storage file, so that the AusweisApp2 can be started from the build directory. CONFIGURE_FILE(${RESOURCES_DIR}/config.json.in ${_destination}/config.json @ONLY) - - # Copy qtlogging.ini file - CONFIGURE_FILE(${RESOURCES_DIR}/qtlogging.ini ${_destination}/qtlogging.ini COPYONLY) ENDFUNCTION() diff --git a/cmake/Helper.cmake b/cmake/Helper.cmake index 4792414..cf37bdf 100644 --- a/cmake/Helper.cmake +++ b/cmake/Helper.cmake @@ -3,7 +3,9 @@ INCLUDE(CheckCXXCompilerFlag) # Check if a compiler flag is supported by current compiler. # # Options -# NOQUOTES: Do not add quotes to the variable (not used if it is a TARGET) +# NOQUOTES: Do not add quotes to the variable (not used if it is a TARGET). +# USE_SAME_FOR_LINKER: Use flag value for linker, too. +# USE_LINKER_ONLY: Use flag for linker only. Only recognized for USE_SAME_FOR_LINKER. # # Parameter # NAME: Add a human readable name. This is for configure output only to @@ -15,7 +17,7 @@ INCLUDE(CheckCXXCompilerFlag) # If VAR parameter is a cmake TARGET the compiler flag will be added # to the COMPILE_FLAGS property of this TARGET only. FUNCTION(ADD_FLAG) - SET(options NOQUOTES) + SET(options NOQUOTES USE_SAME_FOR_LINKER USE_LINKER_ONLY) SET(oneValueArgs NAME) SET(multiValueArgs LINK VAR) cmake_parse_arguments(_PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -36,7 +38,30 @@ FUNCTION(ADD_FLAG) STRING(REPLACE "-" "_" flagname ${flagname}) STRING(REPLACE " " "_" flagname ${flagname}) - CHECK_CXX_COMPILER_FLAG(${flag} ${flagname}) + # GCC will ignore unknown warning options when used in the -Wno- form. It will complain + # about it though, if something else goes wrong. To check if this is a warning which can be + # disabled, we remove the negation for the test only: + STRING(REPLACE "-Wno-" "-W" flagtest ${flag}) + + # This enforces warnings like "-Wunused-command-line-argument" to fail + # the check. + SET(errorflag "") + IF(NOT MSVC) + SET(errorflag "-Werror") + ENDIF() + + IF(_PARAM_USE_SAME_FOR_LINKER) + IF(CMAKE_VERSION VERSION_LESS "3.14") + SET(CMAKE_REQUIRED_LIBRARIES ${_PARAM_LINK} ${flagtest}) + ELSE() + SET(CMAKE_REQUIRED_LINK_OPTIONS ${_PARAM_LINK} ${flagtest}) + ENDIF() + IF(_PARAM_USE_LINKER_ONLY) + SET(flagtest "") + ENDIF() + ENDIF() + + CHECK_CXX_COMPILER_FLAG("${flagtest} ${errorflag}" ${flagname}) IF(${flagname}) FOREACH(var ${_PARAM_VAR}) IF (${var} MATCHES "^AusweisApp") @@ -267,25 +292,27 @@ IF((WIN32 AND NOT WINDOWS_STORE) OR LINUX OR MAC OR CYGWIN OR BSD) ENDIF() -FUNCTION(FILE_SIZE _outSize _file) - IF(LINUX) - SET(SIZE_COMMAND stat -c "%s" "${_file}") - ELSEIF(MAC) - SET(SIZE_COMMAND stat -f "%z" "${_file}") - ELSE() - RETURN() - ENDIF() +IF(CMAKE_VERSION VERSION_LESS "3.14") # Use FILE(SIZE) + FUNCTION(FILE_SIZE _outSize _file) + IF(LINUX) + SET(SIZE_COMMAND stat -c "%s" "${_file}") + ELSEIF(MAC) + SET(SIZE_COMMAND stat -f "%z" "${_file}") + ELSE() + RETURN() + ENDIF() - EXECUTE_PROCESS(COMMAND ${SIZE_COMMAND} - OUTPUT_VARIABLE SIZE_OUTPUT - RESULT_VARIABLE SIZE_RESULT - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) + EXECUTE_PROCESS(COMMAND ${SIZE_COMMAND} + OUTPUT_VARIABLE SIZE_OUTPUT + RESULT_VARIABLE SIZE_RESULT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) - IF(${SIZE_RESULT} EQUAL 0) - SET(${_outSize} ${SIZE_OUTPUT} PARENT_SCOPE) - ENDIF() -ENDFUNCTION() + IF(${SIZE_RESULT} EQUAL 0) + SET(${_outSize} ${SIZE_OUTPUT} PARENT_SCOPE) + ENDIF() + ENDFUNCTION() +ENDIF() IF(NOT COMMAND FIND_HOST_PACKAGE) MACRO(FIND_HOST_PACKAGE) @@ -350,6 +377,7 @@ IF(WIN32) IF(WIN_TIMESTAMP) IF(NOT WIN_TIMESTAMP_URL) + # http://rfc3161timestamp.globalsign.com/advanced SET(WIN_TIMESTAMP_URL http://timestamp.digicert.com) ENDIF() SET(SIGNTOOL_PARAMS ${SIGNTOOL_PARAMS} /tr ${WIN_TIMESTAMP_URL} /td ${WIN_SIGN_HASHALGO}) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 1d08808..33e8f8f 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -40,17 +40,19 @@ SET(DEPENDENCY_CHECK " IF(WIN32) - IF(MSVC) + IF(MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") SET(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .) - IF(NOT CMAKE_VERSION VERSION_LESS "3.6") - SET(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) - ENDIF() + SET(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) INCLUDE(InstallRequiredSystemLibraries) ENDIF() IF(TARGET Qt5::Qml) + FETCH_TARGET_LOCATION(libEgl "Qt5::Gui_EGL") + FETCH_TARGET_LOCATION(libGLES "Qt5::Gui_GLESv2") FETCH_TARGET_LOCATION(libQuickControls2 "Qt5::QuickControls2") INSTALL(FILES ${libQuickControls2} DESTINATION . COMPONENT Runtime) + INSTALL(FILES ${libEgl} DESTINATION . COMPONENT Runtime) + INSTALL(FILES ${libGLES} DESTINATION . COMPONENT Runtime) ENDIF() FETCH_TARGET_LOCATION(libSvg "Qt5::Svg") FETCH_TARGET_LOCATION(pluginSvg "Qt5::QSvgPlugin") @@ -148,7 +150,6 @@ ELSEIF(APPLE AND NOT IOS) LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtNetwork.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtSvg.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtWidgets.framework") - LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtPrintSupport.framework") # remove if disabled in Qt LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtWebSockets.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtConcurrent.framework") IF(TARGET Qt5::Qml) @@ -156,6 +157,9 @@ ELSEIF(APPLE AND NOT IOS) LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtQuick.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtQuickControls2.framework") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtQuickTemplates2.framework") + IF(NOT "${Qt5Core_VERSION}" VERSION_LESS "5.12") + LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtQuickShapes.framework") + ENDIF() ENDIF() IF(TARGET Qt5::Bluetooth) LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "/Contents/Frameworks/QtBluetooth.framework") @@ -164,9 +168,9 @@ ELSEIF(APPLE AND NOT IOS) FETCH_TARGET_LOCATION(opensslCryptoName "OpenSSL::Crypto" NAME) FETCH_TARGET_LOCATION(opensslSslName "OpenSSL::SSL" NAME) IF(CMAKE_VERSION VERSION_LESS "3.13") - SET(OPENSSL_LIB_LOCATION "/Contents/MacOS") + SET(OPENSSL_LIB_LOCATION "/Contents/MacOS") ELSE() - SET(OPENSSL_LIB_LOCATION "/Contents/Frameworks") + SET(OPENSSL_LIB_LOCATION "/Contents/Frameworks") ENDIF() LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "${OPENSSL_LIB_LOCATION}/${opensslCryptoName}") LIST(APPEND ADDITIONAL_BUNDLE_FILES_TO_SIGN "${OPENSSL_LIB_LOCATION}/${opensslSslName}") @@ -206,14 +210,18 @@ ELSEIF(ANDROID) ENDIF() LIST(APPEND JAVA_FILES "${_java_file}") ENDFOREACH() + + INSTALL(FILES ${PACKAGING_DIR}/android/res/values/strings.xml DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/values COMPONENT Runtime) ELSE() SET(ANDROID_MANIFEST AndroidManifest.xml.apk.in) FOREACH(entry ldpi mdpi hdpi xhdpi xxhdpi xxxhdpi) - INSTALL(FILES ${RESOURCES_IMG_ANDROID_DIR}/${entry}/${ANDROID_LAUNCHER_ICON} DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/drawable-${entry} COMPONENT Runtime RENAME npa.png) + INSTALL(FILES ${RESOURCES_IMG_ANDROID_DIR}/${entry}/background_npa.png DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/mipmap-${entry} COMPONENT Runtime RENAME npa_background.png) + INSTALL(FILES ${RESOURCES_IMG_ANDROID_DIR}/${entry}/foreground_${ANDROID_LAUNCHER_ICON} DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/mipmap-${entry} COMPONENT Runtime RENAME npa_foreground.png) + INSTALL(FILES ${RESOURCES_IMG_ANDROID_DIR}/${entry}/${ANDROID_LAUNCHER_ICON} DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/mipmap-${entry} COMPONENT Runtime RENAME npa.png) ENDFOREACH() - INSTALL(FILES ${PACKAGING_DIR}/android/styles.xml DESTINATION ${ANDROID_PACKAGE_SRC_DIR}/res/values COMPONENT Runtime) + INSTALL(DIRECTORY ${PACKAGING_DIR}/android/res DESTINATION ${ANDROID_PACKAGE_SRC_DIR} COMPONENT Runtime) FILE(GLOB_RECURSE JAVA_FILES "${SRC_DIR}/*.java") ENDIF() @@ -228,7 +236,10 @@ ELSEIF(ANDROID) SET(ANDROID_VERSION_NAME ${PROJECT_VERSION}) ENDIF() CONFIGURE_FILE(${PACKAGING_DIR}/android/${ANDROID_MANIFEST} ${ANDROID_PACKAGE_SRC_DIR}/AndroidManifest.xml @ONLY) - CONFIGURE_FILE(${PACKAGING_DIR}/android/fileprovider.xml ${ANDROID_PACKAGE_SRC_DIR}/res/xml/fileprovider.xml COPYONLY) + CONFIGURE_FILE(${PACKAGING_DIR}/android/backup_rules.xml ${ANDROID_PACKAGE_SRC_DIR}/res/xml/backup_rules.xml COPYONLY) + IF(NOT ANDROID_BUILD_AAR) + CONFIGURE_FILE(${PACKAGING_DIR}/android/fileprovider.xml ${ANDROID_PACKAGE_SRC_DIR}/res/xml/fileprovider.xml COPYONLY) + ENDIF() SET(ANDROID_APP_BINARY "${CMAKE_INSTALL_PREFIX}/${ANDROID_DEST}/libAusweisApp2.so") SET(SYMBOL_FOLDER "${CMAKE_BINARY_DIR}/debug.symbols/${CMAKE_ANDROID_ARCH_ABI}") @@ -295,7 +306,7 @@ ENDIF() IF(WIN32) IF(SIGNTOOL_CMD) - CONFIGURE_FILE(${CMAKE_MODULE_PATH}/SignFiles.cmake.in ${CMAKE_BINARY_DIR}/SignFiles.cmake @ONLY) + CONFIGURE_FILE(${CMAKE_DIR}/SignFiles.cmake.in ${CMAKE_BINARY_DIR}/SignFiles.cmake @ONLY) INSTALL(CODE " EXECUTE_PROCESS(COMMAND \"${CMAKE_COMMAND}\" -DSIGN_EXT=*.exe -P \"${CMAKE_BINARY_DIR}/SignFiles.cmake\" WORKING_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${DEFAULT_FILE_DESTINATION}\") @@ -316,11 +327,6 @@ IF(NOT ANDROID_BUILD_AAR) INSTALL(FILES ${RCC} DESTINATION ${DEFAULT_FILE_DESTINATION} COMPONENT Runtime) ENDIF() -IF(NOT ANDROID AND NOT IOS) - # qtlogging.ini - INSTALL(FILES ${RESOURCES_DIR}/qtlogging.ini DESTINATION ${DEFAULT_FILE_DESTINATION} COMPONENT Runtime) -ENDIF() - # qt qml plugins IF(DESKTOP AND TARGET Qt5::Qml) FOREACH(entry QtQuick QtQuick.2 QtQml QtGraphicalEffects Qt) diff --git a/cmake/Libraries.cmake b/cmake/Libraries.cmake index f0e816e..fed98dc 100644 --- a/cmake/Libraries.cmake +++ b/cmake/Libraries.cmake @@ -13,7 +13,7 @@ FIND_PACKAGE(Qt5Svg ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5LinguistTools ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5WebSockets ${MIN_QT_VERSION} REQUIRED) -IF(NOT DESKTOP AND NOT ANDROID_BUILD_AAR OR "${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") +IF(NOT ANDROID_BUILD_AAR) FIND_PACKAGE(Qt5Qml ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5Quick ${MIN_QT_VERSION} REQUIRED) FIND_PACKAGE(Qt5QuickControls2 ${MIN_QT_VERSION} REQUIRED) @@ -78,7 +78,7 @@ ENDIF() IF(MINGW) SET(PCSC_LIBRARIES -lwinscard) SET(WIN_DEFAULT_LIBS "-lAdvapi32" "-lKernel32" "-lOle32" "-lSetupapi" "-lVersion" "-lws2_32") -ELSEIF(MSVC) +ELSEIF(MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") SET(PCSC_LIBRARIES winscard.lib) SET(WIN_DEFAULT_LIBS setupapi.lib version.lib Ws2_32.lib) ELSEIF(ANDROID) @@ -86,6 +86,7 @@ ELSEIF(ANDROID) ELSEIF(IOS) FIND_LIBRARY(IOS_ASSETSLIBRARY AssetsLibrary) FIND_LIBRARY(IOS_UIKIT UIKit) + FIND_LIBRARY(IOS_STOREKIT StoreKit) FIND_LIBRARY(IOS_MOBILECORESERVICES MobileCoreServices) FIND_LIBRARY(IOS_COREBLUETOOTH CoreBluetooth) FIND_LIBRARY(IOS_COREFOUNDATION CoreFoundation) @@ -98,6 +99,8 @@ ELSEIF(IOS) FIND_LIBRARY(IOS_SYSTEMCONFIGURATION SystemConfiguration) FIND_LIBRARY(IOS_AUDIOTOOLBOX AudioToolbox) FIND_LIBRARY(IOS_IMAGEIO ImageIO) + FIND_LIBRARY(IOS_CORENFC CoreNFC) + FIND_LIBRARY(IOS_MESSAGEUI MessageUI) ELSEIF(MAC) FIND_PATH(PCSC_INCLUDE_DIRS WinSCard.h) FIND_LIBRARY(PCSC_LIBRARIES NAMES PCSC WinSCard) diff --git a/cmake/Messages.cmake b/cmake/Messages.cmake index dfc993a..bbacf96 100644 --- a/cmake/Messages.cmake +++ b/cmake/Messages.cmake @@ -9,7 +9,11 @@ MESSAGE(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}") MESSAGE(STATUS "CMAKE_SYSROOT: ${CMAKE_SYSROOT}") MESSAGE(STATUS "CMAKE_SYSROOT_LINK: ${CMAKE_SYSROOT_LINK}") MESSAGE(STATUS "CMAKE_SYSROOT_COMPILE: ${CMAKE_SYSROOT_COMPILE}") +IF(APPLE) + MESSAGE(STATUS "CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT}") +ENDIF() MESSAGE(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") +MESSAGE(STATUS "CMAKE_CXX_SIMULATE_ID: ${CMAKE_CXX_SIMULATE_ID}") IF(ANDROID) MESSAGE(STATUS "CMAKE_ANDROID_NDK: ${CMAKE_ANDROID_NDK}") @@ -25,10 +29,6 @@ IF(ANDROID) MESSAGE(STATUS "ANDROID_NDK_REVISION: ${ANDROID_NDK_REVISION}") MESSAGE(STATUS "ANDROID_SDK_REVISION: ${ANDROID_SDK_REVISION}") - -ELSEIF(IOS) - MESSAGE(STATUS "CMAKE_IOS_SDK_ROOT: ${CMAKE_IOS_SDK_ROOT}") - MESSAGE(STATUS "CMAKE_IOS_DEVELOPER_ROOT: ${CMAKE_IOS_DEVELOPER_ROOT}") ENDIF() diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 0b602c0..c5b3a70 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -96,20 +96,28 @@ IF(WIN32) IF(SIGNTOOL_CMD) MESSAGE(STATUS "MSI can be signed with 'make package.sign'") - ADD_CUSTOM_TARGET(package.sign COMMAND ${SIGNTOOL_CMD} ${SIGNTOOL_PARAMS} ${PROJECT_BINARY_DIR}/${CPACK_PACKAGE_FILE_NAME}.msi) + SET(MSI ${PROJECT_BINARY_DIR}/${CPACK_PACKAGE_FILE_NAME}.msi) + ADD_CUSTOM_TARGET(package.sign COMMAND ${SIGNTOOL_CMD} ${SIGNTOOL_PARAMS} ${MSI} + COMMAND ${SIGNTOOL_CMD} verify /v /pa ${MSI}) ENDIF() ELSEIF(IOS) FILE(WRITE ${PROJECT_BINARY_DIR}/ipa.cmake " - SET(BUNDLE_DIRS \"\${CONFIG}-iphoneos;\${CONFIG};UninstalledProducts;UninstalledProducts/iphoneos\") + FUNCTION(FIND_BUNDLE _name _out_bundle _out_parent_dir) + SET(BUNDLE_DIRS \"\${CONFIG}-iphoneos;\${CONFIG};UninstalledProducts;UninstalledProducts/iphoneos\") - FOREACH(dir \${BUNDLE_DIRS}) - SET(tmpBundleDir ${PROJECT_BINARY_DIR}/src/\${dir}/${PROJECT_NAME}.app) - IF(EXISTS \"\${tmpBundleDir}\") - SET(BundleDir \"\${tmpBundleDir}\") - BREAK() - ENDIF() - ENDFOREACH() + FOREACH(dir \${BUNDLE_DIRS}) + SET(tmpDir ${PROJECT_BINARY_DIR}/src/\${dir}) + SET(tmpBundleDir \${tmpDir}/\${_name}) + IF(EXISTS \"\${tmpBundleDir}\") + SET(\${_out_bundle} \"\${tmpBundleDir}\" PARENT_SCOPE) + SET(\${_out_parent_dir} \"\${tmpDir}\" PARENT_SCOPE) + BREAK() + ENDIF() + ENDFOREACH() + ENDFUNCTION() + + FIND_BUNDLE(${PROJECT_NAME}.app BundleDir ParentDir) IF(BundleDir) MESSAGE(STATUS \"Use bundle: \${BundleDir}\") @@ -117,9 +125,15 @@ ELSEIF(IOS) MESSAGE(FATAL_ERROR \"Bundle directory does not exist\") ENDIF() - EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy_directory \${BundleDir} Payload/AusweisApp2.app) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy_directory \${BundleDir} Payload/${PROJECT_NAME}.app) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar cf \"${CPACK_PACKAGE_FILE_NAME}.ipa\" --format=zip Payload) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E remove_directory Payload) + + FIND_BUNDLE(${PROJECT_NAME}.app.dSYM dSYM ParentDir) + IF(dSYM) + MESSAGE(STATUS \"Use dSYM: \${dSYM}\") + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar cf \"\${CMAKE_BINARY_DIR}/${CPACK_PACKAGE_FILE_NAME}.dSYM.zip\" --format=zip ${PROJECT_NAME}.app.dSYM WORKING_DIRECTORY \${ParentDir}) + ENDIF() ") ADD_CUSTOM_TARGET(ipa COMMAND ${CMAKE_COMMAND} -DCONFIG=$ -P ${CMAKE_BINARY_DIR}/ipa.cmake) @@ -164,26 +178,27 @@ ELSEIF(ANDROID) ENDIF() MESSAGE(STATUS "Using androiddeployqt: ${androiddeployqt}") - OPTION(ANDROID_USE_GRADLE "Use gradle for androiddeployqt" ON) + FILE(READ "${QT_HOST_PREFIX}/src/android/templates/build.gradle" BUILD_GRADLE) - IF(ANDROID_USE_GRADLE) - FILE(READ "${QT_HOST_PREFIX}/src/android/templates/build.gradle" BUILD_GRADLE) + IF(ANDROID_BUILD_AAR) + STRING(REPLACE "apply plugin: 'com.android.application'" "apply plugin: 'com.android.library'" BUILD_GRADLE "${BUILD_GRADLE}") + ENDIF() - IF(ANDROID_BUILD_AAR) - STRING(REPLACE "apply plugin: 'com.android.application'" "apply plugin: 'com.android.library'" BUILD_GRADLE "${BUILD_GRADLE}") - ENDIF() - - FILE(WRITE "${CMAKE_INSTALL_PREFIX}/build.gradle" "${BUILD_GRADLE}") - - FILE(READ "${PACKAGING_DIR}/android/build.gradle.append" BUILD_GRADLE) - FILE(APPEND "${CMAKE_INSTALL_PREFIX}/build.gradle" "${BUILD_GRADLE}") + FILE(WRITE "${CMAKE_INSTALL_PREFIX}/build.gradle" "${BUILD_GRADLE}") + FILE(READ "${PACKAGING_DIR}/android/build.gradle.append" BUILD_GRADLE) + FILE(APPEND "${CMAKE_INSTALL_PREFIX}/build.gradle" "${BUILD_GRADLE}") + OPTION(ANDROID_LINT "Lint Android package" ON) + IF(NOT ANDROID_LINT) + FILE(APPEND "${CMAKE_INSTALL_PREFIX}/build.gradle" "tasks.lint.enabled = false") ENDIF() IF(ANDROID_BUILD_AAR) SET(ANDROID_FILE_EXT aar) CONFIGURE_FILE(${PACKAGING_DIR}/android/pom.xml.in ${CMAKE_INSTALL_PREFIX}/${CPACK_PACKAGE_FILE_NAME}.pom @ONLY) + CONFIGURE_FILE("${PACKAGING_DIR}/android/lint.aar.xml" "${CMAKE_INSTALL_PREFIX}/lint.xml" COPYONLY) ELSE() SET(ANDROID_FILE_EXT apk) + CONFIGURE_FILE("${PACKAGING_DIR}/android/lint.apk.xml" "${CMAKE_INSTALL_PREFIX}/lint.xml" COPYONLY) ENDIF() MESSAGE(STATUS "Prepare ${ANDROID_FILE_EXT} file generation") @@ -195,64 +210,40 @@ ELSEIF(ANDROID) ELSEIF(APK_SIGN_KEYSTORE AND APK_SIGN_KEYSTORE_ALIAS AND APK_SIGN_KEYSTORE_PSW) MESSAGE(STATUS "Release build will be signed using: ${APK_SIGN_KEYSTORE} | Alias: ${APK_SIGN_KEYSTORE_ALIAS}") SET(DEPLOY_CMD_SIGN --sign ${APK_SIGN_KEYSTORE} ${APK_SIGN_KEYSTORE_ALIAS} --storepass ${APK_SIGN_KEYSTORE_PSW} --digestalg SHA-256 --sigalg SHA256WithRSA) - IF(ANDROID_USE_GRADLE) - SET(ANDROID_FILE dist-release-signed.apk) - ELSE() - SET(ANDROID_FILE QtApp-release-signed.apk) - ENDIF() + SET(ANDROID_FILE dist-release-signed.apk) ELSE() - IF(ANDROID_USE_GRADLE) - SET(ANDROID_FILE dist-release-unsigned.apk) - ELSE() - SET(ANDROID_FILE QtApp-release-unsigned.apk) - ENDIF() - + SET(ANDROID_FILE dist-release-unsigned.apk) MESSAGE(WARNING "Cannot sign release build! Set APK_SIGN_KEYSTORE, APK_SIGN_KEYSTORE_ALIAS and APK_SIGN_KEYSTORE_PSW!") ENDIF() ELSE() - IF(ANDROID_USE_GRADLE) - SET(ANDROID_FILE dist-debug.${ANDROID_FILE_EXT}) + SET(ANDROID_FILE dist-debug.${ANDROID_FILE_EXT}) + ENDIF() + + SET(DEPLOY_CMD ${androiddeployqt} --verbose --gradle --input ${ANDROID_DEPLOYMENT_SETTINGS} --output ${CMAKE_INSTALL_PREFIX} ${DEPLOY_CMD_SIGN}) + SET(SOURCE_ANDROID_FILE ${CMAKE_INSTALL_PREFIX}/build/outputs/${ANDROID_FILE_EXT}) + + IF("${Qt5Core_VERSION}" VERSION_GREATER_EQUAL "5.12.0" AND NOT ANDROID_BUILD_AAR) + IF(${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") + SET(SOURCE_ANDROID_FILE ${SOURCE_ANDROID_FILE}/debug) ELSE() - SET(ANDROID_FILE QtApp-debug.apk) + SET(SOURCE_ANDROID_FILE ${SOURCE_ANDROID_FILE}/release) ENDIF() ENDIF() - SET(DEPLOY_CMD ${androiddeployqt} --verbose --input ${ANDROID_DEPLOYMENT_SETTINGS} --output ${CMAKE_INSTALL_PREFIX} ${DEPLOY_CMD_SIGN}) - - IF(ANDROID_USE_GRADLE) - SET(DEPLOY_CMD ${DEPLOY_CMD} --gradle) - SET(SOURCE_ANDROID_FILE ${CMAKE_INSTALL_PREFIX}/build/outputs/${ANDROID_FILE_EXT}) - - IF("${Qt5Core_VERSION}" VERSION_GREATER_EQUAL "5.12.0" AND NOT ANDROID_BUILD_AAR) - IF(${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") - SET(SOURCE_ANDROID_FILE ${SOURCE_ANDROID_FILE}/debug) - ELSE() - SET(SOURCE_ANDROID_FILE ${SOURCE_ANDROID_FILE}/release) - ENDIF() - ENDIF() - - SET(SOURCE_ANDROID_FILE ${SOURCE_ANDROID_FILE}/${ANDROID_FILE}) - ELSE() - IF(ANDROID_BUILD_AAR) - MESSAGE(FATAL_ERROR "Use gradle to build an AAR") - ENDIF() - SET(SOURCE_ANDROID_FILE ${CMAKE_INSTALL_PREFIX}/bin/${ANDROID_FILE}) - ENDIF() + SET(SOURCE_ANDROID_FILE ${SOURCE_ANDROID_FILE}/${ANDROID_FILE}) SET(DESTINATION_ANDROID_FILE ${CMAKE_INSTALL_PREFIX}/${CPACK_PACKAGE_FILE_NAME}.${ANDROID_FILE_EXT}) # Add DEPENDS install someday - # http://public.kitware.com/Bug/view.php?id=8438 + # https://gitlab.kitware.com/cmake/cmake/issues/8438 ADD_CUSTOM_TARGET(${ANDROID_FILE_EXT} COMMAND ${DEPLOY_CMD} COMMAND ${CMAKE_COMMAND} -E copy ${SOURCE_ANDROID_FILE} ${DESTINATION_ANDROID_FILE}) - IF(ANDROID_USE_GRADLE) - ADD_CUSTOM_COMMAND(TARGET ${ANDROID_FILE_EXT} POST_BUILD + ADD_CUSTOM_COMMAND(TARGET ${ANDROID_FILE_EXT} POST_BUILD COMMAND ${CMAKE_INSTALL_PREFIX}/gradlew sourcesJar lint COMMAND ${CMAKE_COMMAND} -E copy build/libs/dist-sources.jar ${CPACK_PACKAGE_FILE_NAME}-sources.jar WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}) - ENDIF() IF(NOT ANDROID_BUILD_AAR) FIND_PROGRAM(apksigner apksigner HINTS ${ANDROID_SDK}/build-tools/${ANDROID_BUILD_TOOLS_REVISION} CMAKE_FIND_ROOT_PATH_BOTH) diff --git a/cmake/Tools.cmake b/cmake/Tools.cmake index a43c7c4..7d5be15 100644 --- a/cmake/Tools.cmake +++ b/cmake/Tools.cmake @@ -1,44 +1,4 @@ IF(COVERAGE) - - # LCov (http://ltp.sourceforge.net/coverage/lcov.php) - FIND_PROGRAM(LCOV_BIN lcov CMAKE_FIND_ROOT_PATH_BOTH) - IF(LCOV_BIN) - SET(LCOV_FILE "${PROJECT_BINARY_DIR}/coverage.info") - SET(LCOV_GLOBAL_CMD ${LCOV_BIN} -q -o ${LCOV_FILE}) - SET(LCOV_CMD ${LCOV_GLOBAL_CMD} -c -d ${PROJECT_BINARY_DIR} -b ${PROJECT_SOURCE_DIR}) - SET(LCOV_RM_CMD ${LCOV_GLOBAL_CMD} -r ${LCOV_FILE} "*/test/*" "*/include/*" "*/src/external/*" "moc_*" "*.moc" "qrc_*" "ui_*") - - IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - IF(WIN32) - SET(CLANG_GCOV py) - ELSE() - SET(CLANG_GCOV sh) - ENDIF() - SET(LCOV_CMD ${LCOV_CMD} --gcov-tool ${RESOURCES_DIR}/jenkins/clang-gcov.${CLANG_GCOV}) - ENDIF() - - ADD_CUSTOM_COMMAND(OUTPUT ${LCOV_FILE} COMMAND ${LCOV_CMD} COMMAND ${LCOV_RM_CMD}) - ADD_CUSTOM_TARGET(lcov COMMAND ${LCOV_BIN} -l ${LCOV_FILE} DEPENDS ${LCOV_FILE}) - - FIND_PROGRAM(GENHTML_BIN genhtml CMAKE_FIND_ROOT_PATH_BOTH) - IF(GENHTML_BIN) - SET(REPORT_DIR "${PROJECT_BINARY_DIR}/coverage.report") - SET(GENHTML_CMD ${GENHTML_BIN} -q -p ${PROJECT_SOURCE_DIR} --num-spaces=4 -o ${REPORT_DIR} ${LCOV_FILE}) - FIND_PROGRAM(FILT_BIN c++filt CMAKE_FIND_ROOT_PATH_BOTH) - IF(FILT_BIN) - SET(GENHTML_CMD ${GENHTML_CMD} --demangle-cpp) - ENDIF() - - ADD_CUSTOM_COMMAND(OUTPUT ${REPORT_DIR} COMMAND ${GENHTML_CMD} DEPENDS ${LCOV_FILE}) - ADD_CUSTOM_TARGET(lcov.report DEPENDS ${REPORT_DIR}) - ENDIF() - - SET(LCOV_XML "${PROJECT_BINARY_DIR}/coverage.xml") - SET(LCOV_COBERTURA_CMD ${RESOURCES_DIR}/jenkins/lcov_cobertura.py ${LCOV_FILE} -b ${PROJECT_SOURCE_DIR} -o ${LCOV_XML}) - ADD_CUSTOM_COMMAND(OUTPUT ${LCOV_XML} COMMAND ${LCOV_COBERTURA_CMD} DEPENDS ${LCOV_FILE}) - ADD_CUSTOM_TARGET(lcov.xml DEPENDS ${LCOV_XML}) - ENDIF() - # gcovr (http://gcovr.com/) FIND_PROGRAM(GCOVR_BIN gcovr CMAKE_FIND_ROOT_PATH_BOTH) IF(GCOVR_BIN) @@ -48,7 +8,6 @@ IF(COVERAGE) ADD_CUSTOM_COMMAND(OUTPUT ${GCOVR_FILE} COMMAND ${GCOVR_CMD} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) ADD_CUSTOM_TARGET(gcovr DEPENDS ${GCOVR_FILE}) ENDIF() - ENDIF() # CppCheck (http://cppcheck.sourceforge.net) @@ -118,32 +77,34 @@ ENDIF() FIND_PROGRAM(UNCRUSTIFY uncrustify CMAKE_FIND_ROOT_PATH_BOTH) IF(UNCRUSTIFY) - FILE(GLOB_RECURSE FILES_JAVA ${PROJECT_SOURCE_DIR}/*.java) - FILE(GLOB_RECURSE FILES_CPP ${PROJECT_SOURCE_DIR}/*.cpp) - FILE(GLOB_RECURSE FILES_H ${PROJECT_SOURCE_DIR}/*.h) - FILE(GLOB_RECURSE FILES_MM ${PROJECT_SOURCE_DIR}/*.mm) - FILE(GLOB_RECURSE FILES_M ${PROJECT_SOURCE_DIR}/*.m) - SET(FILES ${FILES_JAVA} ${FILES_CPP} ${FILES_H} ${FILES_MM} ${FILES_M}) - SET(FORMATTING_FILE ${PROJECT_BINARY_DIR}/formatting.files) - - FILE(WRITE ${FORMATTING_FILE} "") - FOREACH(file ${FILES}) - IF(NOT "${file}" MATCHES "/external/") - FILE(APPEND ${FORMATTING_FILE} ${file}) - FILE(APPEND ${FORMATTING_FILE} "\n") - ENDIF() - ENDFOREACH() - - SET(UNCRUSTIFY_CFG ${PROJECT_SOURCE_DIR}/uncrustify.cfg) - SET(UNCRUSTIFY_CMD ${UNCRUSTIFY} -c ${UNCRUSTIFY_CFG} --replace --no-backup -q -F ${FORMATTING_FILE}) - EXECUTE_PROCESS(COMMAND ${UNCRUSTIFY} --version OUTPUT_VARIABLE UNCRUSTIFY_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) STRING(REPLACE "uncrustify " "" UNCRUSTIFY_VERSION ${UNCRUSTIFY_VERSION}) + STRING(REPLACE "Uncrustify-" "" UNCRUSTIFY_VERSION ${UNCRUSTIFY_VERSION}) - SET(UNCRUSTIFY_NEEDED_VERSION "0.68") + SET(UNCRUSTIFY_NEEDED_VERSION "0.69") IF("${UNCRUSTIFY_VERSION}" STRLESS "${UNCRUSTIFY_NEEDED_VERSION}") MESSAGE(WARNING "Uncrustify seems to be too old. Use at least ${UNCRUSTIFY_NEEDED_VERSION}... you are using: ${UNCRUSTIFY_VERSION}") ELSE() + MESSAGE(STATUS "Found uncrustify ${UNCRUSTIFY_VERSION}") + FILE(GLOB_RECURSE FILES_JAVA ${PROJECT_SOURCE_DIR}/*.java) + FILE(GLOB_RECURSE FILES_CPP ${PROJECT_SOURCE_DIR}/*.cpp) + FILE(GLOB_RECURSE FILES_H ${PROJECT_SOURCE_DIR}/*.h) + FILE(GLOB_RECURSE FILES_H_IN ${PROJECT_SOURCE_DIR}/*.h.in) + FILE(GLOB_RECURSE FILES_MM ${PROJECT_SOURCE_DIR}/*.mm) + FILE(GLOB_RECURSE FILES_M ${PROJECT_SOURCE_DIR}/*.m) + SET(FILES ${FILES_JAVA} ${FILES_CPP} ${FILES_H_IN} ${FILES_H} ${FILES_MM} ${FILES_M}) + SET(FORMATTING_FILE ${PROJECT_BINARY_DIR}/formatting.files) + + FILE(WRITE ${FORMATTING_FILE} "") + FOREACH(file ${FILES}) + IF(NOT "${file}" MATCHES "/external/") + FILE(APPEND ${FORMATTING_FILE} ${file}) + FILE(APPEND ${FORMATTING_FILE} "\n") + ENDIF() + ENDFOREACH() + + SET(UNCRUSTIFY_CFG ${PROJECT_SOURCE_DIR}/uncrustify.cfg) + SET(UNCRUSTIFY_CMD ${UNCRUSTIFY} -c ${UNCRUSTIFY_CFG} --replace --no-backup -q -F ${FORMATTING_FILE}) ADD_CUSTOM_TARGET(format COMMAND ${UNCRUSTIFY_CMD} SOURCES ${UNCRUSTIFY_CFG} ${FILES}) ENDIF() ENDIF() @@ -172,108 +133,138 @@ ENDFUNCTION() FIND_PROGRAM(CONVERT convert CMAKE_FIND_ROOT_PATH_BOTH) IF(CONVERT) - IF(IOS) - SET(CONVERT_CMD convert -alpha off) - SET(BACKGROUND_COLOR "#5489c2") - ELSE() - SET(CONVERT_CMD convert) - SET(BACKGROUND_COLOR "transparent") - ENDIF() + SET(BACKGROUND_COLOR "#dcebf6") + + ADD_CUSTOM_TARGET(npaicons.docs + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 320 -resize 96x96 npa.svg npa_docs.png + WORKING_DIRECTORY ${RESOURCES_DIR}/images) ADD_CUSTOM_TARGET(npaicons.win - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -define icon:auto-resize=256,96,64,48,40,32,24,20,16 npa.svg npa.ico + COMMAND ${CONVERT} -background transparent -define icon:auto-resize=256,96,64,48,40,32,24,20,16 npa.svg npa.ico WORKING_DIRECTORY ${RESOURCES_DIR}/images) - ADD_CUSTOM_TARGET(npaicons.android.preview - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 36x36 npa_preview.svg android/ldpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 160 -resize 48x48 npa_preview.svg android/mdpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 240 -resize 72x72 npa_preview.svg android/hdpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 320 -resize 96x96 npa_preview.svg android/xhdpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 480 -resize 144x144 npa_preview.svg android/xxhdpi/npa_preview.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 640 -resize 192x192 npa_preview.svg android/xxxhdpi/npa_preview.png + ADD_CUSTOM_TARGET(npaicons.android.background + COMMAND ${CONVERT} -background transparent -resize 81x81 android/npa_background.svg android/ldpi/background_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 160 -resize 108x108 android/npa_background.svg android/mdpi/background_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 240 -resize 162x162 android/npa_background.svg android/hdpi/background_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 320 -resize 216x216 android/npa_background.svg android/xhdpi/background_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 480 -resize 324x324 android/npa_background.svg android/xxhdpi/background_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 640 -resize 432x432 android/npa_background.svg android/xxxhdpi/background_npa.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) - ADD_CUSTOM_TARGET(npaicons.android.beta - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 36x36 npa_beta.svg android/ldpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 160 -resize 48x48 npa_beta.svg android/mdpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 240 -resize 72x72 npa_beta.svg android/hdpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 320 -resize 96x96 npa_beta.svg android/xhdpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 480 -resize 144x144 npa_beta.svg android/xxhdpi/npa_beta.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 640 -resize 192x192 npa_beta.svg android/xxxhdpi/npa_beta.png + ADD_CUSTOM_TARGET(npaicons.android.foreground.preview + COMMAND ${CONVERT} -background transparent -resize 54x54 -gravity center -extent 81x81 android/npa_preview.svg android/ldpi/foreground_npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 160 -resize 72x72 -gravity center -extent 108x108 android/npa_preview.svg android/mdpi/foreground_npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 240 -resize 108x108 -gravity center -extent 162x162 android/npa_preview.svg android/hdpi/foreground_npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 320 -resize 144x144 -gravity center -extent 216x216 android/npa_preview.svg android/xhdpi/foreground_npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 480 -resize 216x216 -gravity center -extent 324x324 android/npa_preview.svg android/xxhdpi/foreground_npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 640 -resize 288x288 -gravity center -extent 432x432 android/npa_preview.svg android/xxxhdpi/foreground_npa_preview.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) - ADD_CUSTOM_TARGET(npaicons.android - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 36x36 npa.svg android/ldpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 160 -resize 48x48 npa.svg android/mdpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 240 -resize 72x72 npa.svg android/hdpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 320 -resize 96x96 npa.svg android/xhdpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 480 -resize 144x144 npa.svg android/xxhdpi/npa.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 640 -resize 192x192 npa.svg android/xxxhdpi/npa.png + ADD_CUSTOM_TARGET(npaicons.android.legacy.preview + COMMAND ${CONVERT} -background transparent -resize 36x36 npa_preview.svg android/ldpi/npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 160 -resize 48x48 npa_preview.svg android/mdpi/npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 240 -resize 72x72 npa_preview.svg android/hdpi/npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 320 -resize 96x96 npa_preview.svg android/xhdpi/npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 480 -resize 144x144 npa_preview.svg android/xxhdpi/npa_preview.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 640 -resize 192x192 npa_preview.svg android/xxxhdpi/npa_preview.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) + ADD_CUSTOM_TARGET(npaicons.android.foreground.beta + COMMAND ${CONVERT} -background transparent -resize 54x54 -gravity center -extent 81x81 android/npa_beta.svg android/ldpi/foreground_npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 160 -resize 72x72 -gravity center -extent 78x78 android/npa_beta.svg android/mdpi/foreground_npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 240 -resize 108x108 -gravity center -extent 162x162 android/npa_beta.svg android/hdpi/foreground_npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 320 -resize 144x144 -gravity center -extent 216x216 android/npa_beta.svg android/xhdpi/foreground_npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 480 -resize 216x216 -gravity center -extent 324x324 android/npa_beta.svg android/xxhdpi/foreground_npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 640 -resize 288x288 -gravity center -extent 432x432 android/npa_beta.svg android/xxxhdpi/foreground_npa_beta.png + WORKING_DIRECTORY ${RESOURCES_DIR}/images) + + ADD_CUSTOM_TARGET(npaicons.android.legacy.beta + COMMAND ${CONVERT} -background transparent -resize 36x36 npa_beta.svg android/ldpi/npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 160 -resize 48x48 npa_beta.svg android/mdpi/npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 240 -resize 72x72 npa_beta.svg android/hdpi/npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 320 -resize 96x96 npa_beta.svg android/xhdpi/npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 480 -resize 144x144 npa_beta.svg android/xxhdpi/npa_beta.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 640 -resize 192x192 npa_beta.svg android/xxxhdpi/npa_beta.png + WORKING_DIRECTORY ${RESOURCES_DIR}/images) + + + ADD_CUSTOM_TARGET(npaicons.android.foreground + COMMAND ${CONVERT} -background transparent -resize 54x54 -gravity center -extent 81x81 android/npa.svg android/ldpi/foreground_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 160 -resize 72x72 -gravity center -extent 108x108 android/npa.svg android/mdpi/foreground_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 240 -resize 108x108 -gravity center -extent 162x162 android/npa.svg android/hdpi/foreground_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 320 -resize 144x144 -gravity center -extent 216x216 android/npa.svg android/xhdpi/foreground_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 480 -resize 216x216 -gravity center -extent 324x324 android/npa.svg android/xxhdpi/foreground_npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 640 -resize 288x288 -gravity center -extent 432x432 android/npa.svg android/xxxhdpi/foreground_npa.png + WORKING_DIRECTORY ${RESOURCES_DIR}/images) + + ADD_CUSTOM_TARGET(npaicons.android.legacy + COMMAND ${CONVERT} -background transparent -resize 36x36 npa.svg android/ldpi/npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 160 -resize 48x48 npa.svg android/mdpi/npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 240 -resize 72x72 npa.svg android/hdpi/npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 320 -resize 96x96 npa.svg android/xhdpi/npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 480 -resize 144x144 npa.svg android/xxhdpi/npa.png + COMMAND ${CONVERT} -background transparent -units PixelsPerInch -resample 640 -resize 192x192 npa.svg android/xxxhdpi/npa.png + WORKING_DIRECTORY ${RESOURCES_DIR}/images) + + ADD_CUSTOM_TARGET(npaicons.playstore + COMMAND ${CONVERT} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 1120 -resize 336x336 -gravity center -extent 512x512 ${RESOURCES_DIR}/images/npa.svg playstore.png + COMMAND ${CONVERT} -background '${BACKGROUND_COLOR}' -units PixelsPerInch -resample 1120 -resize 336x336 -gravity center -extent 512x512 ${RESOURCES_DIR}/images/npa_preview.svg playstore_preview.png + ) + ADD_CUSTOM_TARGET(npaicons.ios.beta - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 20x20 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 40x40 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 60x60 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 29x29 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 58x58 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 87x87 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 40x40 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 80x80 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 120x120 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 120x120 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 180x180 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 76x76 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 152x152 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 167x167 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 1024x1024 npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 20x20 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 40x40 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 60x60 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 29x29 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 58x58 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 87x87 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 40x40 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 80x80 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 120x120 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 120x120 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 180x180 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 76x76 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 152x152 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 167x167 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 1024x1024 iOS/appIcons/npa_beta.svg iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png + COMMAND ${CONVERT} -background none -resize 256x256 npa_beta.svg iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage.png + COMMAND ${CONVERT} -background none -resize 512x512 npa_beta.svg iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@2x.png + COMMAND ${CONVERT} -background none -resize 768x768 npa_beta.svg iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@3x.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) ADD_CUSTOM_TARGET(npaicons.ios - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 20x20 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 40x40 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 60x60 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 29x29 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 58x58 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 87x87 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 40x40 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 80x80 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 120x120 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 120x120 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 180x180 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 76x76 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 152x152 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 167x167 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 1024x1024 npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 20x20 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 40x40 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 60x60 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 29x29 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 58x58 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 87x87 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 40x40 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 80x80 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 120x120 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 120x120 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 180x180 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 76x76 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 152x152 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 167x167 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png + COMMAND ${CONVERT} -alpha off -background '${BACKGROUND_COLOR}' -resize 1024x1024 iOS/appIcons/npa.svg iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png + COMMAND ${CONVERT} -background none -resize 256x256 npa.svg iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage.png + COMMAND ${CONVERT} -background none -resize 512x512 npa.svg iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@2x.png + COMMAND ${CONVERT} -background none -resize 768x768 npa.svg iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@3x.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) - SET(BACKGROUND_COLOR "rgb\(220,235,246\)") - - ADD_CUSTOM_TARGET(npaicons.iphone - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 320x320 -gravity center -extent 640x1136 npa.svg iOS/launchImages/Default-568h@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 320x320 -gravity center -extent 640x1136 npa.svg iOS/launchImages/launchImage568@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 480x480 -gravity center -extent 960x1704 npa.svg iOS/launchImages/launchImage568@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 353x353 -gravity center -extent 705x1334 npa.svg iOS/launchImages/launchImage667@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 563x563 -gravity center -extent 1125x2001 npa.svg iOS/launchImages/launchImage667@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 414x414 -gravity center -extent 828x1472 npa.svg iOS/launchImages/launchImage736@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 621x621 -gravity center -extent 1242x2208 npa.svg iOS/launchImages/launchImage736@3x.png - WORKING_DIRECTORY ${RESOURCES_DIR}/images) - - ADD_CUSTOM_TARGET(npaicons.ipad - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 768x768 -gravity center -extent 2048x1536 npa.svg iOS/launchImages/launchImage1024@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 1152x1152 -gravity center -extent 3072x2304 npa.svg iOS/launchImages/launchImage1024@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 834x834 -gravity center -extent 2224x1668 npa.svg iOS/launchImages/launchImage1112@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 1251x1251 -gravity center -extent 3336x2502 npa.svg iOS/launchImages/launchImage1112@3x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 1024x1024 -gravity center -extent 2732x2048 npa.svg iOS/launchImages/launchImage1366@2x.png - COMMAND ${CONVERT_CMD} -background '${BACKGROUND_COLOR}' -resize 1536x1536 -gravity center -extent 4098x3072 npa.svg iOS/launchImages/launchImage1366@3x.png - WORKING_DIRECTORY ${RESOURCES_DIR}/images) - - ADD_CUSTOM_TARGET(npaicons DEPENDS npaicons.win npaicons.ios npaicons.ios.beta npaicons.iphone npaicons.ipad npaicons.android npaicons.android.beta npaicons.android.preview) + ADD_CUSTOM_TARGET(npaicons DEPENDS npaicons.docs npaicons.win npaicons.ios npaicons.ios.beta npaicons.playstore npaicons.android.background npaicons.android.foreground npaicons.android.foreground.beta npaicons.android.foreground.preview npaicons.android.legacy npaicons.android.legacy.beta npaicons.android.legacy.preview) ENDIF() FIND_PROGRAM(PNGQUANT pngquant CMAKE_FIND_ROOT_PATH_BOTH) IF(PNGQUANT) -SET(PNGQUANT_CMD pngquant -f -o) + SET(PNGQUANT_CMD pngquant -f -o) + + ADD_CUSTOM_TARGET(pngquant.npaicons.docs + COMMAND ${PNGQUANT_CMD} npa_docs.png -- npa_docs.png + WORKING_DIRECTORY ${RESOURCES_DIR}/images) + ADD_CUSTOM_TARGET(pngquant.ios.beta COMMAND ${PNGQUANT_CMD} iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png -- iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png -- iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png @@ -290,6 +281,9 @@ SET(PNGQUANT_CMD pngquant -f -o) COMMAND ${PNGQUANT_CMD} iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png -- iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png -- iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png -- iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage.png -- iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage.png + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@2x.png -- iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@2x.png + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@3x.png -- iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@3x.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) ADD_CUSTOM_TARGET(pngquant.ios @@ -308,28 +302,30 @@ SET(PNGQUANT_CMD pngquant -f -o) COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png -- iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/Default-568h@2x.png -- iOS/launchImages/Default-568h@2x.png + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage.png -- iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage.png + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@2x.png -- iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@2x.png + COMMAND ${PNGQUANT_CMD} iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@3x.png -- iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@3x.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) - ADD_CUSTOM_TARGET(pngquant.iphone - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage568@2x.png -- iOS/launchImages/launchImage568@2x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage568@3x.png -- iOS/launchImages/launchImage568@3x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage667@2x.png -- iOS/launchImages/launchImage667@2x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage667@3x.png -- iOS/launchImages/launchImage667@3x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage736@2x.png -- iOS/launchImages/launchImage736@2x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage736@3x.png -- iOS/launchImages/launchImage736@3x.png + ADD_CUSTOM_TARGET(pngquant.android.background + COMMAND ${PNGQUANT_CMD} android/ldpi/background_npa.png -- android/ldpi/background_npa.png + COMMAND ${PNGQUANT_CMD} android/mdpi/background_npa.png -- android/mdpi/background_npa.png + COMMAND ${PNGQUANT_CMD} android/hdpi/background_npa.png -- android/hdpi/background_npa.png + COMMAND ${PNGQUANT_CMD} android/xhdpi/background_npa.png -- android/xhdpi/background_npa.png + COMMAND ${PNGQUANT_CMD} android/xxhdpi/background_npa.png -- android/xxhdpi/background_npa.png + COMMAND ${PNGQUANT_CMD} android/xxxhdpi/background_npa.png -- android/xxxhdpi/background_npa.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) - ADD_CUSTOM_TARGET(pngquant.ipad - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage1024@2x.png -- iOS/launchImages/launchImage1024@2x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage1024@3x.png -- iOS/launchImages/launchImage1024@3x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage1112@2x.png -- iOS/launchImages/launchImage1112@2x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage1112@3x.png -- iOS/launchImages/launchImage1112@3x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage1366@2x.png -- iOS/launchImages/launchImage1366@2x.png - COMMAND ${PNGQUANT_CMD} iOS/launchImages/launchImage1366@3x.png -- iOS/launchImages/launchImage1366@3x.png + ADD_CUSTOM_TARGET(pngquant.android.foreground.preview + COMMAND ${PNGQUANT_CMD} android/ldpi/foreground_npa_preview.png -- android/ldpi/foreground_npa_preview.png + COMMAND ${PNGQUANT_CMD} android/mdpi/foreground_npa_preview.png -- android/mdpi/foreground_npa_preview.png + COMMAND ${PNGQUANT_CMD} android/hdpi/foreground_npa_preview.png -- android/hdpi/foreground_npa_preview.png + COMMAND ${PNGQUANT_CMD} android/xhdpi/foreground_npa_preview.png -- android/xhdpi/foreground_npa_preview.png + COMMAND ${PNGQUANT_CMD} android/xxhdpi/foreground_npa_preview.png -- android/xxhdpi/foreground_npa_preview.png + COMMAND ${PNGQUANT_CMD} android/xxxhdpi/foreground_npa_preview.png -- android/xxxhdpi/foreground_npa_preview.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) - ADD_CUSTOM_TARGET(pngquant.android.preview + ADD_CUSTOM_TARGET(pngquant.android.legacy.preview COMMAND ${PNGQUANT_CMD} android/ldpi/npa_preview.png -- android/ldpi/npa_preview.png COMMAND ${PNGQUANT_CMD} android/mdpi/npa_preview.png -- android/mdpi/npa_preview.png COMMAND ${PNGQUANT_CMD} android/hdpi/npa_preview.png -- android/hdpi/npa_preview.png @@ -338,7 +334,16 @@ SET(PNGQUANT_CMD pngquant -f -o) COMMAND ${PNGQUANT_CMD} android/xxxhdpi/npa_preview.png -- android/xxxhdpi/npa_preview.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) - ADD_CUSTOM_TARGET(pngquant.android.beta + ADD_CUSTOM_TARGET(pngquant.android.foreground.beta + COMMAND ${PNGQUANT_CMD} android/ldpi/foreground_npa_beta.png -- android/ldpi/foreground_npa_beta.png + COMMAND ${PNGQUANT_CMD} android/mdpi/foreground_npa_beta.png -- android/mdpi/foreground_npa_beta.png + COMMAND ${PNGQUANT_CMD} android/hdpi/foreground_npa_beta.png -- android/hdpi/foreground_npa_beta.png + COMMAND ${PNGQUANT_CMD} android/xhdpi/foreground_npa_beta.png -- android/xhdpi/foreground_npa_beta.png + COMMAND ${PNGQUANT_CMD} android/xxhdpi/foreground_npa_beta.png -- android/xxhdpi/foreground_npa_beta.png + COMMAND ${PNGQUANT_CMD} android/xxxhdpi/foreground_npa_beta.png -- android/xxxhdpi/foreground_npa_beta.png + WORKING_DIRECTORY ${RESOURCES_DIR}/images) + + ADD_CUSTOM_TARGET(pngquant.android.legacy.beta COMMAND ${PNGQUANT_CMD} android/ldpi/npa_beta.png -- android/ldpi/npa_beta.png COMMAND ${PNGQUANT_CMD} android/mdpi/npa_beta.png -- android/mdpi/npa_beta.png COMMAND ${PNGQUANT_CMD} android/hdpi/npa_beta.png -- android/hdpi/npa_beta.png @@ -347,7 +352,16 @@ SET(PNGQUANT_CMD pngquant -f -o) COMMAND ${PNGQUANT_CMD} android/xxxhdpi/npa_beta.png -- android/xxxhdpi/npa_beta.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) - ADD_CUSTOM_TARGET(pngquant.android + ADD_CUSTOM_TARGET(pngquant.android.foreground + COMMAND ${PNGQUANT_CMD} android/ldpi/foreground_npa.png -- android/ldpi/foreground_npa.png + COMMAND ${PNGQUANT_CMD} android/mdpi/foreground_npa.png -- android/mdpi/foreground_npa.png + COMMAND ${PNGQUANT_CMD} android/hdpi/foreground_npa.png -- android/hdpi/foreground_npa.png + COMMAND ${PNGQUANT_CMD} android/xhdpi/foreground_npa.png -- android/xhdpi/foreground_npa.png + COMMAND ${PNGQUANT_CMD} android/xxhdpi/foreground_npa.png -- android/xxhdpi/foreground_npa.png + COMMAND ${PNGQUANT_CMD} android/xxxhdpi/foreground_npa.png -- android/xxxhdpi/foreground_npa.png + WORKING_DIRECTORY ${RESOURCES_DIR}/images) + + ADD_CUSTOM_TARGET(pngquant.android.legacy COMMAND ${PNGQUANT_CMD} android/ldpi/npa.png -- android/ldpi/npa.png COMMAND ${PNGQUANT_CMD} android/mdpi/npa.png -- android/mdpi/npa.png COMMAND ${PNGQUANT_CMD} android/hdpi/npa.png -- android/hdpi/npa.png @@ -356,7 +370,7 @@ SET(PNGQUANT_CMD pngquant -f -o) COMMAND ${PNGQUANT_CMD} android/xxxhdpi/npa.png -- android/xxxhdpi/npa.png WORKING_DIRECTORY ${RESOURCES_DIR}/images) - ADD_CUSTOM_TARGET(pngquant DEPENDS pngquant.ios pngquant.ios.beta pngquant.iphone pngquant.ipad pngquant.android pngquant.android.beta pngquant.android.preview) + ADD_CUSTOM_TARGET(pngquant DEPENDS pngquant.npaicons.docs pngquant.ios pngquant.ios.beta pngquant.android.background pngquant.android.foreground pngquant.android.foreground.beta pngquant.android.foreground.preview pngquant.android.legacy pngquant.android.legacy.beta pngquant.android.legacy.preview) ENDIF() IF(NOT JAVA_EXECUTABLE) diff --git a/cmake/android.toolchain.cmake b/cmake/android.toolchain.cmake index 4515882..9b86823 100644 --- a/cmake/android.toolchain.cmake +++ b/cmake/android.toolchain.cmake @@ -54,6 +54,7 @@ ENDIF() OPTION(ANDROID_BUILD_AAR "Build AAR file instead of APK" OFF) -SET(CMAKE_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH} CACHE string "android find search path root") +SET(CMAKE_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH} CACHE STRING "android find search path root") SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/cmake/iOS.toolchain.cmake b/cmake/iOS.toolchain.cmake index e1968f1..38c5232 100644 --- a/cmake/iOS.toolchain.cmake +++ b/cmake/iOS.toolchain.cmake @@ -1,217 +1,35 @@ -# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake -# files which are included with CMake 2.8.4 -# It has been altered for iOS development +CMAKE_MINIMUM_REQUIRED(VERSION 3.14) -# Options: -# -# IOS_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64 -# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders -# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch. -# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. -# -# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder -# By default this location is automatcially chosen based on the IOS_PLATFORM value above. -# If set manually, it will override the default location and force the user of a particular Developer Platform -# -# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder -# By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value. -# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path. -# If set manually, this will force the use of a specific SDK version +SET(CMAKE_SYSTEM_NAME iOS) +SET(CMAKE_OSX_ARCHITECTURES "arm64") +SET(CMAKE_OSX_DEPLOYMENT_TARGET 13.0) -# Macros: -# -# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE) -# A convenience macro for setting xcode specific properties on targets -# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1") -# -# find_host_package (PROGRAM ARGS) -# A macro used to find executable programs on the host system, not within the iOS environment. -# Thanks to the android-cmake project for providing the command +SET(UNIX True) +SET(APPLE True) +SET(IOS True) -# Standard settings -set (CMAKE_SYSTEM_NAME Darwin) -set (CMAKE_SYSTEM_VERSION 1) -set (UNIX True) -set (APPLE True) -set (IOS True) +SET(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) -find_program(xcrun_bin xcrun CMAKE_FIND_ROOT_PATH_BOTH) -if(NOT xcrun_bin) - message(FATAL_ERROR "Cannot find xcrun") -endif() +SET(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES") +SET(CMAKE_XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE "bitcode") -function(XCRUN _out _find) - execute_process(COMMAND ${xcrun_bin} --sdk iphoneos -f ${_find} OUTPUT_VARIABLE tmp_out OUTPUT_STRIP_TRAILING_WHITESPACE) - set(${_out} "${tmp_out}" PARENT_SCOPE) -endfunction() +SET(CMAKE_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH} CACHE STRING "iOS find search path root") +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) -# https://cmake.org/Bug/view.php?id=15329 -set(CMAKE_MACOSX_BUNDLE YES) -set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") - -# Required as of cmake 2.8.10 -set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE) - -# Determine the cmake host system version so we know where to find the iOS SDKs -find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin) -if (CMAKE_UNAME) - exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION) - string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") -endif (CMAKE_UNAME) - -XCRUN(CMAKE_C_COMPILER clang) -XCRUN(CMAKE_CXX_COMPILER clang++) -XCRUN(CMAKE_AR ar) -set(CMAKE_AR ${CMAKE_AR} CACHE FILEPATH "" FORCE) - -# All iOS/Darwin specific settings - some may be redundant -set (CMAKE_SHARED_LIBRARY_PREFIX "lib") -set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") -set (CMAKE_SHARED_MODULE_PREFIX "lib") -set (CMAKE_SHARED_MODULE_SUFFIX ".so") -set (CMAKE_MODULE_EXISTS 1) -set (CMAKE_DL_LIBS "") - -set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") -set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") -set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") -set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") - -# Hidden visibilty is required for cxx on iOS -set (CMAKE_C_FLAGS_INIT "") -set (CMAKE_CXX_FLAGS_INIT "-fvisibility=hidden -fvisibility-inlines-hidden") - -set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") -set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") - -set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) -set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names") -set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names") -set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") -set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") -set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") - -# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree -# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache -# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun) -# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex -if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) - find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) -endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) - -# Setup iOS platform unless specified manually with IOS_PLATFORM -if (NOT DEFINED IOS_PLATFORM) - set (IOS_PLATFORM "OS") -endif (NOT DEFINED IOS_PLATFORM) -set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform") - -# Setup building for arm64 or not -if (NOT DEFINED BUILD_ARM64) - set (BUILD_ARM64 true) -endif (NOT DEFINED BUILD_ARM64) -set (BUILD_ARM64 ${BUILD_ARM64} CACHE STRING "Build arm64 arch or not") - -# Check the platform selection and setup for developer root -if (${IOS_PLATFORM} STREQUAL "OS") - set (IOS_PLATFORM_LOCATION "iPhoneOS.platform") - - # This causes the installers to properly locate the output libraries - set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos") -elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR") - set (SIMULATOR true) - set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") - - # This causes the installers to properly locate the output libraries - set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") -elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR64") - set (SIMULATOR true) - set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") - - # This causes the installers to properly locate the output libraries - set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") -else (${IOS_PLATFORM} STREQUAL "OS") - message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR") -endif (${IOS_PLATFORM} STREQUAL "OS") - -# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT -# Note Xcode 4.3 changed the installation location, choose the most recent one available -exec_program(/usr/bin/xcode-select ARGS -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR) -set (XCODE_POST_43_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer") -set (XCODE_PRE_43_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") -if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) - if (EXISTS ${XCODE_POST_43_ROOT}) - set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_POST_43_ROOT}) - elseif(EXISTS ${XCODE_PRE_43_ROOT}) - set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_PRE_43_ROOT}) - endif (EXISTS ${XCODE_POST_43_ROOT}) -endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) -set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform") - -# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT -if (NOT DEFINED CMAKE_IOS_SDK_ROOT) - file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*") - if (_CMAKE_IOS_SDKS) - list (SORT _CMAKE_IOS_SDKS) - list (REVERSE _CMAKE_IOS_SDKS) - list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT) - else (_CMAKE_IOS_SDKS) - message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.") - endif (_CMAKE_IOS_SDKS) - message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}") -endif (NOT DEFINED CMAKE_IOS_SDK_ROOT) -set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK") - -# Set the sysroot default to the most recent SDK -set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support") - -# set the architecture for iOS -if (${IOS_PLATFORM} STREQUAL "OS") - set (IOS_ARCH arm64) -elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR") - set (IOS_ARCH i386) -elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR64") - set (IOS_ARCH x86_64) -endif (${IOS_PLATFORM} STREQUAL "OS") - -set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS") - -# Set the find root to the iOS developer roots and to user defined paths -set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root") - -# default to searching for frameworks first -set (CMAKE_FIND_FRAMEWORK FIRST) - -# set up the default search directories for frameworks -set (CMAKE_SYSTEM_FRAMEWORK_PATH - ${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks - ${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks - ${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks -) - -# only search the iOS sdks, not the remainder of the host filesystem -set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) -set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - - -# This little macro lets you set any XCode specific property -macro (set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE) - set_property (TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE}) -endmacro (set_xcode_property) - - -# This macro lets you find executable programs on the host system -macro (find_host_package) - set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) - set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) - set (IOS FALSE) - - find_package(${ARGN}) - - set (IOS TRUE) - set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) - set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endmacro (find_host_package) +# work-around: cmake will fail if this is missing! +macro(find_host_package) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER) + set(IOS FALSE) + find_package(${ARGN}) + set(IOS TRUE) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) +endmacro(find_host_package) diff --git a/docs/installation/README.de.rst b/docs/installation/README.de.rst index 9207e70..54d01b1 100644 --- a/docs/installation/README.de.rst +++ b/docs/installation/README.de.rst @@ -233,7 +233,7 @@ CA-Zertifikate im Windows-Truststore werden daher ignoriert. "eID-SDK", TCP, 24727, "eingehend", "Nein", "Verwendung der SDK-Schnittstelle", "Nur erreichbar von localhost [#TR-03124]_" "SaK1", UDP, 24727, "eingehend", "Ja", "Smartphone als Kartenleser, Erkennung [#TR-03112]_", "Broadcasts" "SaK2", TCP, , "ausgehend", "Ja", "Smartphone als Kartenleser, Verwendung [#TR-03112]_", "Verbindung im lokalen Subnetz" - "Update", TCP, 443, "ausgehend", "Ja", "Updates [#govurl]_ zu Dienstanbietern und Kartenlesegeräten sowie Informationen zu neuen AusweisApp2-Versionen [#updatecheck]_ .", "Die Zertifikate der TLS-Verbindung werden mit in der AusweisApp2 mitgelieferten CA-Zertifikaten validiert. Im Betriebssystem hinterlegte CA-Zertifikate werden ignoriert." + "Update", TCP, 443, "ausgehend", "Ja", "Updates [#govurl]_ zu Dienstanbietern und Kartenlesern sowie Informationen zu neuen AusweisApp2-Versionen [#updatecheck]_ .", "Die Zertifikate der TLS-Verbindung werden mit in der AusweisApp2 mitgelieferten CA-Zertifikaten validiert. Im Betriebssystem hinterlegte CA-Zertifikate werden ignoriert." .. [#TR-03124] Siehe TR-03124 des BSI .. [#TR-03112] Siehe TR-03112-6 des BSI diff --git a/docs/installation/conf.py.in b/docs/installation/conf.py.in index e6308d8..a36922f 100644 --- a/docs/installation/conf.py.in +++ b/docs/installation/conf.py.in @@ -152,7 +152,7 @@ latex_documents = [ # The name of an image file (relative to this directory) to place at the top of # the title page. -latex_logo = '@SPHINX_DOCS_DIR@/../../resources/images/android/xhdpi/npa.png' +latex_logo = '@SPHINX_DOCS_DIR@/../../resources/images/npa_docs.png' # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. diff --git a/docs/releasenotes/1.16.2.rst b/docs/releasenotes/1.16.2.rst index 6e1f39e..c7c2fc7 100644 --- a/docs/releasenotes/1.16.2.rst +++ b/docs/releasenotes/1.16.2.rst @@ -1,7 +1,7 @@ AusweisApp2 1.16.2 ^^^^^^^^^^^^^^^^^^ -**Releasedatum:** 15. April 2019 +**Releasedatum:** 15. Mai 2019 diff --git a/docs/releasenotes/1.18.0.rst b/docs/releasenotes/1.18.0.rst new file mode 100644 index 0000000..19db2dd --- /dev/null +++ b/docs/releasenotes/1.18.0.rst @@ -0,0 +1,21 @@ +AusweisApp2 1.18.0 +^^^^^^^^^^^^^^^^^^ + +**Releasedatum:** 24. September 2019 + + + +Anwender +"""""""" + - Aktivierung der NFC-Funktion für iOS 13. + + - Kleinere Fehlerbehebungen. + + +Entwickler +"""""""""" + - Aktualisierung von OpenSSL auf die Version 1.1.1c. + + - Aktualisierung von Qt auf die Version 5.12.4. + + - Ein Compiler mit C++17-Support ist erforderlich. diff --git a/docs/releasenotes/_themes/appcast/layout.html b/docs/releasenotes/_themes/appcast/layout.html index 4d7b89d..d5ca210 100644 --- a/docs/releasenotes/_themes/appcast/layout.html +++ b/docs/releasenotes/_themes/appcast/layout.html @@ -3,6 +3,11 @@ AusweisApp2 Release Notes +
diff --git a/docs/releasenotes/announce.rst b/docs/releasenotes/announce.rst index 8bd7a82..6c7be64 100644 --- a/docs/releasenotes/announce.rst +++ b/docs/releasenotes/announce.rst @@ -1,7 +1,7 @@ Abkündigungen ============= -Mit der Version 1.18.0 der AusweisApp2 wird die Unterstützung +Mit der Version 1.20.0 der AusweisApp2 wird die Unterstützung folgender Systeme eingestellt. - OS X 10.11 diff --git a/docs/releasenotes/appcast.rst b/docs/releasenotes/appcast.rst index 31532dc..e655b2b 100644 --- a/docs/releasenotes/appcast.rst +++ b/docs/releasenotes/appcast.rst @@ -4,8 +4,6 @@ Release Notes .. toctree:: :maxdepth: 1 - 1.16.2 - 1.16.1 - 1.16.0 + 1.18.0 announce issues diff --git a/docs/releasenotes/conf.py.in b/docs/releasenotes/conf.py.in index b8b889b..10c2a0e 100644 --- a/docs/releasenotes/conf.py.in +++ b/docs/releasenotes/conf.py.in @@ -151,7 +151,7 @@ latex_documents = [ # The name of an image file (relative to this directory) to place at the top of # the title page. -latex_logo = '@SPHINX_DOCS_DIR@/../../resources/images/android/xhdpi/npa.png' +latex_logo = '@SPHINX_DOCS_DIR@/../../resources/images/npa_docs.png' # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. diff --git a/docs/releasenotes/general.rst b/docs/releasenotes/general.rst index b656bf6..8d9c533 100644 --- a/docs/releasenotes/general.rst +++ b/docs/releasenotes/general.rst @@ -5,7 +5,7 @@ Die AusweisApp2 ist eine Software, die Sie auf Ihrem Computer installieren, um s Ihrem Personalausweis bzw. Ihrem elektronischen Aufenthaltstitel online auszuweisen. Für die Nutzung der Online-Ausweisfunktion benötigen die Nutzerinnen und Nutzer eine -Software, mit deren Hilfe eine sichere Verbindung zwischen Kartenlesegerät oder Smartphone, +Software, mit deren Hilfe eine sichere Verbindung zwischen Kartenleser oder Smartphone, Personalausweis und eID-Diensteanbieter hergestellt werden kann. Sie ermöglicht den verschlüsselten Datenaustausch zwischen Personalausweis und eID-Dienst. diff --git a/docs/releasenotes/issues.rst b/docs/releasenotes/issues.rst index a8d5b80..06c7e18 100644 --- a/docs/releasenotes/issues.rst +++ b/docs/releasenotes/issues.rst @@ -1,8 +1,20 @@ Bekannte Fehler =============== +.. important:: + Auf Windows wird beim Update einer Version der AusweisApp2 älter + als Version 1.16.0 auf eine aktuelle Version der AusweisApp2 die + ältere Version nicht erkannt und auch nicht entfernt. Bitte gehen + Sie in die Systemsteuerung Ihres Endgeräts und deinstallieren Sie + die ältere Version manuell. Danach kann die bereits installierte, + aktuelle Version aufgerufen werden. + Die nachfolgende Liste bezieht sich auf die aktuelle Version der AusweisApp2. + - Mit der NFC-Schnittstelle des iPhone 7 und iPhone 7 Plus werden viele + Ausweise nicht erkannt (hauptsächlich NFC-A) und es kommt zu Abbrüchen der + Authentisierung. + - Auf Windows 10 Plattformen mit aktivierter Benutzerkontensteuerung kann es bei Benutzern mit eingeschränkten Berechtigungen zu Problemen mit der Online-Ausweisfunktion kommen. @@ -19,8 +31,8 @@ Die nachfolgende Liste bezieht sich auf die aktuelle Version der AusweisApp2. leichten Irritationen bei der angegebenen Bedienung kommen. - Wenn die AusweisApp2 heruntergefahren wird, während eine Authentisierung - oder eine PIN-Änderung mit Komfort-Kartenlesegerät durchgeführt wird, - kann es unter Windows und macOS zu einem Absturz kommen. + oder eine PIN-Änderung mit Komfort-Kartenleser durchgeführt wird, kann es + unter Windows und macOS zu einem Absturz kommen. - Unter Umständen kommt es zu Stabilitätsproblemen der NFC-Schnittstelle auf Android. @@ -31,6 +43,17 @@ Die nachfolgende Liste bezieht sich auf die aktuelle Version der AusweisApp2. - Unter macOS wird der Hinweis bei falscher PIN-Bestätigung während der PIN-Änderung nur sehr kurz angezeigt. - - Bei der Nutzung eines entfernten Kartenlesegeräts mit aktiviertem - Tastaturmodus kann bei einer Authentisierung nicht in die PIN-Änderung - gewechselt werden, wenn nur eine 5-stellige PIN vorhanden ist. + - Bei der Nutzung eines entfernten Kartenlesers mit aktiviertem Tastaturmodus + kann bei einer Authentisierung nicht in die PIN-Änderung gewechselt werden, + wenn nur eine 5-stellige PIN vorhanden ist. + + - Unter Windows wird bei der neuen grafischen Oberfläche der + "Datei speichern unter"-Dialog nicht automatisch bei Beginn einer + Authentisierung geschlossen. + + - Unter macOS können per Tastatur mit den Standardsystemeinstellungen nur + Textfelder angesprungen werden. Mit Änderung der Tastaturnavigationsoption + unter "Systemeinstellungen/Tastatur/Kurzbefehle" auf "Alle Steuerungen" kann + das Verhalten von macOS geändert werden, sodass auch alle anderen + Komponenten in der App fokussiert werden können. + diff --git a/docs/releasenotes/support.rst b/docs/releasenotes/support.rst index 9a41d43..ce5296d 100644 --- a/docs/releasenotes/support.rst +++ b/docs/releasenotes/support.rst @@ -8,8 +8,6 @@ der AusweisApp2 unterstützt. Betriebssysteme """"""""""""""" - - OS X 10.11 - - macOS 10.12 - macOS 10.13 @@ -60,9 +58,9 @@ getestet. -Kartenlesegeräte -~~~~~~~~~~~~~~~~ -Alle Kartenlesegeräte, die die Onlineausweisfunktionalität unterstützen und nach +Kartenleser +~~~~~~~~~~~ +Alle Kartenleser, die die Onlineausweisfunktionalität unterstützen und nach BSI TR-03119 zertifiziert sind. Details hierzu befinden sich auf der Homepage des BSI unter "Nach Technischen Richtlinien zertifizierte Produkte". @@ -93,7 +91,7 @@ Aktuelle Informationen zu Kartenlesern finden Sie auf unserer Webseite: https://www.ausweisapp.bund.de/fragen-und-antworten/voraussetzungen/ Alle NFC-fähigen Smartphones bzw. Tablets, die die Onlineausweisfunktionalität -unterstützen, können als Kartenlesegerät verwendet werden. +unterstützen, können als Kartenleser verwendet werden. Dabei ist es notwendig die mobile AusweisApp2 auf dem jeweiligen Smartphone zu installieren und zu starten. @@ -122,17 +120,17 @@ folgenden Browser zu verwenden. -Kartenlesegeräte -~~~~~~~~~~~~~~~~ +Kartenleser +~~~~~~~~~~~ Alle NFC-fähigen Smartphones bzw. Tablets, die die Onlineausweisfunktionalität unterstützen. Details hierzu befinden sich auf der Homepage: https://www.ausweisapp.bund.de/mobile-geraete/ -Ebenfalls ist es möglich ein weiteres Smartphone als Kartenlesegerät zu -verwenden. Dabei ist es notwendig die mobile AusweisApp2 auf dem jeweiligen -Smartphone zu installieren und zu starten. +Ebenfalls ist es möglich ein weiteres Smartphone als Kartenleser zu verwenden. +Dabei ist es notwendig die mobile AusweisApp2 auf dem jeweiligen Smartphone zu +installieren und zu starten. -Darüber hinaus ist die Verwendung eines Bluetooth-Kartenlesegeräts möglich. -Folgendes Bluetooth-Kartenlesegerät wird von der AusweiApp2 unterstützt: +Darüber hinaus ist die Verwendung eines Bluetooth-Kartenlesers möglich. +Folgender Bluetooth-Kartenleser wird von der AusweiApp2 unterstützt: - cyberJack wave diff --git a/docs/releasenotes/versions.rst b/docs/releasenotes/versions.rst index c1056bd..2d4f6d3 100644 --- a/docs/releasenotes/versions.rst +++ b/docs/releasenotes/versions.rst @@ -1,6 +1,14 @@ Versionen ========= +Versionszweig 1.18 +------------------ +.. toctree:: + :maxdepth: 1 + + 1.18.0 + + Versionszweig 1.16 ------------------ .. toctree:: diff --git a/docs/sdk/android.rst b/docs/sdk/android.rst index 7592c5d..a824e89 100644 --- a/docs/sdk/android.rst +++ b/docs/sdk/android.rst @@ -659,7 +659,7 @@ in the listing below. Implementation """""""""""""" -As it is common on the Android platform, information is send to applications +As it is common on the Android platform, information is sent to applications encapsulated in instances of the **Intent** class. In order to process newly discovered NFC tags, Intents which are given to the application need to be checked for the parcelable NFC extra as shown in the code listing below. diff --git a/docs/sdk/conf.py.in b/docs/sdk/conf.py.in index 44ac321..2b8291d 100644 --- a/docs/sdk/conf.py.in +++ b/docs/sdk/conf.py.in @@ -149,7 +149,7 @@ latex_documents = [ # The name of an image file (relative to this directory) to place at the top of # the title page. -latex_logo = '@SPHINX_DOCS_DIR@/../../resources/images/android/xhdpi/npa.png' +latex_logo = '@SPHINX_DOCS_DIR@/../../resources/images/npa_docs.png' # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. diff --git a/docs/sdk/messages.rst b/docs/sdk/messages.rst index da71caa..3c37806 100644 --- a/docs/sdk/messages.rst +++ b/docs/sdk/messages.rst @@ -235,7 +235,7 @@ a result and an url parameter to indicate the end of an authentication. "minor": "http://www.bsi.bund.de/ecard/api/1.1/resultminor/al/common#internalError", "language": "en", "description": "An internal error has occurred during processing.", - "message": "The ID card has been removed. The process is aborted." + "message": "The connection to the ID card has been lost. The process was aborted." }, "url": "https://test.governikus-eid.de/gov_autent/async?refID=_abcdefgh" } diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 89c0f8e..e0428ff 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.5.0) +CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0) IF(POLICY CMP0010) CMAKE_POLICY(SET CMP0010 NEW) @@ -82,13 +82,12 @@ ELSE() SET(PATCH_CMD ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/patch.py --debug -v) MESSAGE(STATUS "Cannot find 'patch' command... using patch.py") ENDIF() +SET(PATCH_CMD ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PROJECT_SOURCE_DIR}/patches) IF(NOT DESTINATION_DIR) SET(DESTINATION_DIR ${PROJECT_BINARY_DIR}/dist) ENDIF() -SET(PATCHES_DIR ${PROJECT_SOURCE_DIR}/../patches) - IF(NOT PACKAGES_DIR) SET(PACKAGES_DIR $ENV{PACKAGES_DIR}) IF(NOT PACKAGES_DIR) @@ -103,11 +102,11 @@ INCLUDE(Messages) ################################## Versions -SET(QT 5.11.2) -SET(QT_HASH c6104b840b6caee596fa9a35bc5f57f67ed5a99d6a36497b6fe66f990a53ca81) +SET(QT 5.12.4) +SET(QT_HASH 85da5e0ee498759990180d5b8192efaa6060a313c5018b772f57d446bdd425e1) -SET(OPENSSL 1.1.1) -SET(OPENSSL_HASH 2836875a0f89c03d0fdf483941512613a50cfb421d6fd94b9f41d7279d586a3d) +SET(OPENSSL 1.1.1c) +SET(OPENSSL_HASH f6fb3079ad15076154eda9413fed42877d668e7069d9b87396d0804fdb3f4c90) ################################## Files SET(QT_FILE qt-everywhere-src-${QT}.tar.xz) @@ -117,7 +116,7 @@ SET(OPENSSL_FILE openssl-${OPENSSL}.tar.gz) IF("${QT}" MATCHES "alpha|beta|rc") SET(QT_DEST_DIR development_releases) ELSE() - SET(QT_DEST_DIR official_releases) + SET(QT_DEST_DIR archive) # official_releases ENDIF() STRING(SUBSTRING ${QT} 0 4 QT_SUBVERSION) @@ -131,7 +130,8 @@ SET(ENABLED_TARGETS) ######################################################################### LIST(APPEND ENABLED_TARGETS openssl) -SET(OPENSSL_CONFIGURE_FLAGS no-camellia no-bf no-aria no-seed no-poly1305 no-srp no-gost no-ocsp no-idea no-mdc2 no-rc2 no-rc4 no-rc5 no-srtp no-hw no-sm2 no-sm3 no-sm4) +SET(OPENSSL_CONFIGURE_FLAGS no-camellia no-bf no-aria no-seed no-poly1305 no-srp no-gost no-idea no-mdc2 no-rc2 no-rc4 no-rc5 no-srtp no-hw no-sm2 no-sm3 no-sm4) +SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-ocsp no-ct no-dgram) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-cast no-chacha no-blake2 no-rmd160 no-scrypt no-siphash no-whirlpool no-md4 no-des) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-tls1 no-tls1-method no-tls1_1 no-tls1_1-method no-tls1_3 no-ssl3 no-ssl3-method no-dtls no-dtls1-method no-dtls1_2-method) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-deprecated no-engine no-async no-dso no-comp no-ts no-makedepend no-tests shared) @@ -139,6 +139,7 @@ SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-deprecated no-engine n IF(${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") SET(OPENSSL_CONFIGURE_FLAGS --debug ${OPENSSL_CONFIGURE_FLAGS}) ELSE() + SET(OPENSSL_CONFIGURE_FLAGS no-ui-console no-filenames ${OPENSSL_CONFIGURE_FLAGS}) ADD_FLAG(-Os NOQUOTES VAR OPENSSL_COMPILER_FLAGS) ENDIF() @@ -146,14 +147,20 @@ ADD_FLAG(-fstack-protector-strong -fstack-protector NOQUOTES VAR OPENSSL_COMPILE IF(IOS) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} ios64-cross) - SET(OPENSSL_ENV export CROSS_TOP=${CMAKE_IOS_DEVELOPER_ROOT} && export CROSS_SDK=iPhoneOS.sdk &&) + STRING(REGEX REPLACE "/SDKs/.*" "" CROSS_TOP_DEV_ROOT "${CMAKE_OSX_SYSROOT}") + SET(OPENSSL_ENV export CROSS_TOP=${CROSS_TOP_DEV_ROOT} && export CROSS_SDK=iPhoneOS.sdk &&) ELSEIF(APPLE) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} darwin64-x86_64-cc) - SET(OPENSSL_COMPILER_FLAGS ${OPENSSL_COMPILER_FLAGS} -mmacosx-version-min=10.11) + SET(OPENSSL_COMPILER_FLAGS ${OPENSSL_COMPILER_FLAGS} -mmacosx-version-min=10.12) ELSEIF(MINGW) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} mingw) ELSEIF(MSVC) - SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-asm VC-WIN32) + SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-asm) + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} VC-WIN64A) + ELSE() + SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} VC-WIN32) + ENDIF() ELSEIF(ANDROID) IF(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a") SET(OPENSSL_ARCH android-arm) @@ -180,10 +187,10 @@ ELSEIF(ANDROID) ELSEIF(BSD) SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} BSD-x86_64) ELSEIF(LINUX) - IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "i686") - SET(OPENSSL_ARCH linux-generic32) - ELSE() + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) SET(OPENSSL_ARCH linux-x86_64) + ELSE() + SET(OPENSSL_ARCH linux-generic32) ENDIF() SET(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} ${OPENSSL_ARCH}) ELSE() @@ -195,11 +202,8 @@ ExternalProject_Add(openssl URL_HASH SHA256=${OPENSSL_HASH} DOWNLOAD_DIR ${PACKAGES_DIR} - PATCH_COMMAND ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-android-shlib_variant.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-Ignore-disabled-ciphers.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-DSA-mod-inverse-fix.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-Timing-vulnerability-in-DSA-signature-generation-CVE.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/openssl-Timing-vulnerability-in-ECDSA-signature-generation-C.patch + PATCH_COMMAND ${PATCH_CMD}/openssl-android-shlib_variant.patch && + ${PATCH_CMD}/openssl-Adjust-iOS-target.patch CONFIGURE_COMMAND ${OPENSSL_ENV} ${PERL_EXECUTABLE} Configure --prefix=${DESTINATION_DIR} ${OPENSSL_CONFIGURE_FLAGS} "${OPENSSL_COMPILER_FLAGS}" BUILD_COMMAND ${OPENSSL_ENV} ${MAKE} ${MAKE_JOBS} BUILD_IN_SOURCE 1 @@ -235,32 +239,45 @@ LIST(APPEND ENABLED_TARGETS qt) IF(${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") SET(QT_CONFIGURE_FLAGS -debug -qml-debug) - SET(QT_PATCH_COMMAND ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Enable-debug-output-for-OpenSSL.patch &&) + SET(QT_PATCH_COMMAND ${PATCH_CMD}/qt-Enable-debug-output-for-OpenSSL.patch &&) ELSE() SET(QT_CONFIGURE_FLAGS -release -optimize-size -no-qml-debug) SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} $<$:-force-debug-info>) ENDIF() -SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -prefix ${DESTINATION_DIR} -opensource -confirm-license -c++std c++11 -qt-zlib -no-mtdev -qt-libpng -qt-libjpeg -no-dbus -no-harfbuzz -qt-pcre -system-proxies -no-compile-examples -nomake examples -nomake tests -no-sql-sqlite -openssl-linked -I ${DESTINATION_DIR}/include -L ${DESTINATION_DIR}/lib) +SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -prefix ${DESTINATION_DIR} -opensource -confirm-license) +SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -nomake examples -nomake tests -no-mtdev -no-dbus -no-harfbuzz -no-compile-examples -no-sql-sqlite) +SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -qt-zlib -qt-libpng -qt-libjpeg -qt-pcre) +SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -system-proxies -openssl-linked -I ${DESTINATION_DIR}/include -L ${DESTINATION_DIR}/lib) -IF(NOT ANDROID) - LIST(APPEND NO_FEATURES bearermanagement) +IF(CMAKE_CXX_COMPILER_LAUNCHER STREQUAL "ccache") + SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -ccache) ENDIF() -LIST(APPEND NO_FEATURES ftp paint_debug lcdnumber mdiarea) + +LIST(APPEND NO_FEATURES dtls winrt_bt ftp paint_debug lcdnumber mdiarea) LIST(APPEND NO_FEATURES calendarwidget colordialog cups dial fontcombobox fontdialog) LIST(APPEND NO_FEATURES imageformat_bmp imageformat_ppm imageformat_xbm) -LIST(APPEND NO_FEATURES sharedmemory textodfwriter filesystemwatcher) +LIST(APPEND NO_FEATURES sharedmemory textodfwriter bearermanagement) LIST(APPEND NO_FEATURES undocommand undogroup undostack undoview) LIST(APPEND NO_FEATURES printer printdialog printpreviewdialog printpreviewwidget) -LIST(APPEND NO_FEATURES splashscreen syntaxhighlighter dom sql) +LIST(APPEND NO_FEATURES splashscreen syntaxhighlighter dom sql qdoc) FOREACH(feature ${NO_FEATURES}) SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -no-feature-${feature}) ENDFOREACH() -SET(QT_CONFIGURE_FLAGS_OTHER -no-journald -no-directfb -no-linuxfb) -SET(QT_CONFIGURE_FLAGS_SKIP_MODULES -skip qtwebglplugin -skip qtscxml -skip qtxmlpatterns -skip qtwebchannel -skip qtwebengine -skip qtscript -skip qtactiveqt -skip qtlocation -skip qtserialbus -skip qtserialport -skip qtgamepad -skip qtvirtualkeyboard -skip qtcanvas3d -skip qtcharts -skip qtdatavis3d -skip qt3d -skip qtpurchasing -skip qtwayland -skip qtremoteobjects -skip qtspeech -skip qtwebview -skip multimedia -skip qtquickcontrols) +LIST(APPEND SKIP_MODULES qtwebglplugin qtscxml qtxmlpatterns qtwebchannel) +LIST(APPEND SKIP_MODULES qtwebengine qtscript qtactiveqt qtlocation qtserialbus) +LIST(APPEND SKIP_MODULES qtserialport qtgamepad qtvirtualkeyboard qtcanvas3d qtcharts) +LIST(APPEND SKIP_MODULES qtdatavis3d qt3d qtpurchasing qtwayland qtremoteobjects) +LIST(APPEND SKIP_MODULES qtspeech qtwebview multimedia qtquickcontrols) +FOREACH(module ${SKIP_MODULES}) + SET(QT_CONFIGURE_FLAGS_SKIP_MODULES ${QT_CONFIGURE_FLAGS_SKIP_MODULES} -skip ${module}) +ENDFOREACH() + + +SET(QT_CONFIGURE_FLAGS_OTHER -no-journald -no-directfb -no-linuxfb) SET(QT_CONFIGURE ./configure) IF(IOS) SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_OTHER} -sdk iphoneos -xplatform macx-ios-clang) @@ -282,6 +299,8 @@ ELSEIF(WIN32) SET(QT_PLATFORM win32-msvc2015) ELSEIF(MSVC_TOOLSET_VERSION STREQUAL "141") SET(QT_PLATFORM win32-msvc2017) + ELSEIF(MSVC_TOOLSET_VERSION STREQUAL "142") + SET(QT_PLATFORM win32-msvc2019) ELSE() MESSAGE(FATAL_ERROR "Version of MSVC not supported") ENDIF() @@ -295,6 +314,8 @@ ELSEIF(WIN32) SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_OPENSSL} -opengl dynamic -no-icu -no-sql-odbc -platform ${QT_PLATFORM}) SET(QT_CONFIGURE configure.bat) ELSEIF(ANDROID) + FIND_PACKAGE(Java COMPONENTS Development REQUIRED) + IF(CMAKE_COMPILER_IS_GNUCXX) SET(ANDROID_XPLATFORM android-g++) ELSE() @@ -310,8 +331,12 @@ ELSEIF(ANDROID) ENDIF() SET(QT_ENV export OPENSSL_LIBS=-lcrypto-gov\ -lssl-gov &&) -ELSE() +ELSEIF(BSD) + SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_OTHER}) +ELSEIF(LINUX) SET(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_OTHER} -no-libproxy) +ELSE() + MESSAGE(FATAL_ERROR "Unsupported system") ENDIF() IF(IOS OR ANDROID) @@ -325,23 +350,18 @@ ExternalProject_Add(qt DOWNLOAD_DIR ${PACKAGES_DIR} PATCH_COMMAND ${QT_PATCH_COMMAND} - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Disable-unused-imageformats.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Add-IsoDep-to-the-techList-on-Android.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Introduce-reportError-to-fix-QMetaObject-invokeMethod.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Add-work-around-for-freebsd-build.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-configure-refactor-directx-checks.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-configure-detect-fxc.exe-more-thoroughly.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-disable-designer.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-fix-macOS-no-printer.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Use-QUrl-toString-when-forming-the-Host-header.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-QUrl-Support-IPv6-addresses-with-zone-id.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Android-fix-compile-with-NDK-r18.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Android-Fix-crash.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-QObject-Fix-isSignalConnected-when-signals-have-been.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Use-user-provided-session-data-if-available.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Work-Around-FreeBSD-v12-build.patch && - ${PATCH_CMD} -p1 ${PATCH_OPTIONS} ${PATCHES_DIR}/qt-Linux-Remove-our-use-of-syscall-for-statx-2-and-rena.patch && + ${PATCH_CMD}/qt-Disable-unused-imageformats.patch && + ${PATCH_CMD}/qt-Add-work-around-for-freebsd-build.patch && + ${PATCH_CMD}/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch && + ${PATCH_CMD}/qt-Remove-unused-plugins-from-the-build.patch && + ${PATCH_CMD}/qt-disable-designer.patch && + ${PATCH_CMD}/qt-Disable-qmltime-for-shared-build.patch && + ${PATCH_CMD}/qt-Add-Q_CORE_EXPORT-to-lcEventDispatcher.patch && + ${PATCH_CMD}/qt-Fix-build-with-no-feature-printer.patch && + ${PATCH_CMD}/qt-Fix-build-with-no-feature-printer-on-macOS.patch && + ${PATCH_CMD}/qt-Core-IO-Bluetooth-fix-ambiguous-conversions.patch && + ${PATCH_CMD}/qt-Core-IO-Bluetooth-fix-ambiguous-conversions-for-macO.patch && + ${PATCH_CMD}/qt-Adjust-iOS-target.patch && ${CMAKE_COMMAND} -E touch qtbase/.gitignore CONFIGURE_COMMAND ${QT_ENV} ${QT_CONFIGURE} ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_SKIP_MODULES} BUILD_COMMAND ${MAKE} ${MAKE_JOBS} @@ -356,7 +376,11 @@ FOREACH(var ${ENABLED_TARGETS}) EXTERNALPROJECT_GET_PROPERTY(${var} INSTALL_DIR) LIST(APPEND CLEAN_TARGETS ${INSTALL_DIR}) ENDFOREACH() -SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${DESTINATION_DIR};${CLEAN_TARGETS}") +IF(CMAKE_VERSION VERSION_LESS "3.15") + SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${DESTINATION_DIR};${CLEAN_TARGETS}") +ELSE() + SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_CLEAN_FILES "${DESTINATION_DIR};${CLEAN_TARGETS}") +ENDIF() OPTION(COMPRESS_DEPENDS "Disable DEPENDS for compress target" ON) IF(COMPRESS_DEPENDS) @@ -377,9 +401,7 @@ IF(DVCS_FOUND) ENDIF() ENDIF() -IF(IOS) - SET(SYSTEM_NAME iOS) -ELSEIF(ANDROID) +IF(ANDROID) SET(SYSTEM_NAME ${CMAKE_SYSTEM_NAME}_${CMAKE_CXX_COMPILER_ID}_${CMAKE_ANDROID_ARCH_ABI}) ELSE() SET(SYSTEM_NAME ${CMAKE_SYSTEM_NAME}_${CMAKE_CXX_COMPILER_ID}) @@ -392,8 +414,14 @@ IF(WIN32) ENDIF() ENDIF() -SET(COMPRESSION cfJ) -SET(COMPRESSION_FILENDING tar.xz) +IF(CMAKE_VERSION VERSION_LESS "3.15") + SET(COMPRESSION cfJ) + SET(COMPRESSION_FILENDING tar.xz) +ELSE() + SET(COMPRESSION cf) + SET(COMPRESSION_OPTION --zstd) + SET(COMPRESSION_FILENDING tar.zstd) +ENDIF() ADD_CUSTOM_TARGET(compress.pre ${compressed_filename} COMMAND ${CMAKE_COMMAND} -E remove_directory "${DESTINATION_DIR}/doc" COMMAND ${CMAKE_COMMAND} -E remove_directory "${DESTINATION_DIR}/share" @@ -403,6 +431,6 @@ ADD_CUSTOM_TARGET(compress.pre ${compressed_filename} SET(compressed_filename Toolchain_${SYSTEM_NAME}_${stamp}.${COMPRESSION_FILENDING}) ADD_CUSTOM_COMMAND(OUTPUT ${compressed_filename} - COMMAND ${CMAKE_COMMAND} -E tar "${COMPRESSION}" "${compressed_filename}" "${DESTINATION_DIR}" + COMMAND ${CMAKE_COMMAND} -E tar "${COMPRESSION}" "${compressed_filename}" ${COMPRESSION_OPTION} "${DESTINATION_DIR}" DEPENDS compress.pre) ADD_CUSTOM_TARGET(compress DEPENDS ${compressed_filename}) diff --git a/libs/README.rst b/libs/README.rst index ef17004..8185201 100644 --- a/libs/README.rst +++ b/libs/README.rst @@ -4,15 +4,13 @@ Libraries Um die AusweisApp2 zu bauen ist eine Toolchain erforderlich, die die Abhängigkeiten und die Compilertools beinhaltet. -Unterstützte Compiler: +Unterstützte C++17 Compiler: -- MinGW 32 / 64 >= 4.9 +- MinGW / GCC >= 7.3 -- GCC >= 4.9 +- Clang >= 5.0 -- Clang >= 3.4 - -- MSVC >= 2015 +- MSVC >= 2017 Notwendige Bibliotheken: @@ -32,7 +30,7 @@ Notwendige Bibliotheken: Notwendige Tools: -- CMake >= 3.5.0 (>= 3.7.1 für Android) +- CMake >= 3.8.0 (3.14.0 >= für iOS) - http://www.cmake.org @@ -102,7 +100,7 @@ MinGW - http://sourceforge.net/projects/mingw-w64/ - Dabei wurde das folgende Paket getestet: - https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/5.3.0/threads-posix/dwarf/i686-5.3.0-release-posix-dwarf-rt_v4-rev0.7z/download + https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/7.3.0/threads-posix/dwarf/i686-7.3.0-release-posix-dwarf-rt_v5-rev0.7z/download MSYS2 @@ -147,7 +145,7 @@ Vorbereitung :: - Für das Windows SDK 10.0.15063.0 und neuer: + Für das Windows SDK 10.0.15063.0 und neuer (getestet: 10.0.17763.0): WindowsSdkVerBinPath = C:\Program Files (x86)\Windows Kits\10\bin\%VERSION% Für alle älteren Versionen: WindowsSdkDir = C:\Program Files (x86)\Windows Kits\10 diff --git a/libs/patch.py b/libs/patch.py index 4e355f2..f2e957b 100644 --- a/libs/patch.py +++ b/libs/patch.py @@ -368,7 +368,7 @@ class PatchSet(object): header.append(fe.line) fe.next() if fe.is_empty: - if p == None: + if p is None: debug("no patch data found") # error is shown later self.errors += 1 else: @@ -956,7 +956,6 @@ class PatchSet(object): if exists(backupname): warning("can't backup original file to %s - aborting" % backupname) else: - import shutil shutil.move(filename, backupname) if self.write_hunks(backupname, filename, p.hunks): info("successfully patched %d/%d:\t %s" % (i+1, total, filename)) @@ -1020,7 +1019,6 @@ class PatchSet(object): lineno = 1 line = fp.readline() - hno = None try: for hno, h in enumerate(hunks): # skip to first line of the hunk diff --git a/libs/patches/openssl-Adjust-iOS-target.patch b/libs/patches/openssl-Adjust-iOS-target.patch new file mode 100644 index 0000000..94dce17 --- /dev/null +++ b/libs/patches/openssl-Adjust-iOS-target.patch @@ -0,0 +1,25 @@ +From 0628b87d65feb0209303e91bf7529628c4dd80f4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= +Date: Fri, 1 Feb 2019 13:27:04 +0100 +Subject: [PATCH] Adjust iOS target + +--- + Configurations/15-ios.conf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git x/Configurations/15-ios.conf y/Configurations/15-ios.conf +index 1bb9f48d06..fb0ece9aff 100644 +--- x/Configurations/15-ios.conf ++++ y/Configurations/15-ios.conf +@@ -24,7 +24,7 @@ my %targets = ( + "ios64-xcrun" => { + inherit_from => [ "ios-common", asm("aarch64_asm") ], + CC => "xcrun -sdk iphoneos cc", +- cflags => add("-arch arm64 -mios-version-min=7.0.0 -fno-common"), ++ cflags => add("-arch arm64 -mios-version-min=13.0 -fno-common -fembed-bitcode"), + bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", + perlasm_scheme => "ios64", + }, +-- +2.20.1 + diff --git a/libs/patches/openssl-android-shlib_variant.patch b/libs/patches/openssl-android-shlib_variant.patch new file mode 100644 index 0000000..b91afe2 --- /dev/null +++ b/libs/patches/openssl-android-shlib_variant.patch @@ -0,0 +1,25 @@ +From f63dce0a614ad26176dfbf4069264a4ec36c11b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= +Date: Tue, 20 Nov 2018 16:05:20 +0100 +Subject: [PATCH] android shlib_variant + +--- + Configurations/15-android.conf | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git x/Configurations/15-android.conf y/Configurations/15-android.conf +index 7b496a4529..559e11e27f 100644 +--- x/Configurations/15-android.conf ++++ y/Configurations/15-android.conf +@@ -170,6 +170,8 @@ my %targets = ( + bn_ops => sub { android_ndk()->{bn_ops} }, + bin_cflags => "-pie", + enable => [ ], ++ shlib_variant => '-gov', ++ shared_extension => '.so', + }, + "android-arm" => { + ################################################################ +-- +2.20.1 + diff --git a/libs/patches/qt-Add-Q_CORE_EXPORT-to-lcEventDispatcher.patch b/libs/patches/qt-Add-Q_CORE_EXPORT-to-lcEventDispatcher.patch new file mode 100644 index 0000000..4d14b1d --- /dev/null +++ b/libs/patches/qt-Add-Q_CORE_EXPORT-to-lcEventDispatcher.patch @@ -0,0 +1,38 @@ +From 81839e57d5f3d86d6663f58d5d6b7b0605a6986e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= +Date: Tue, 30 Jul 2019 12:49:14 +0200 +Subject: [PATCH] Add Q_CORE_EXPORT to lcEventDispatcher* + +Undefined symbols for architecture arm64: + "lcEventDispatcher()", referenced from: + _qt_main_wrapper in libqios.a(qioseventdispatcher.o) + +[QIOSApplicationStateTracker applicationDidFinishLaunching:] in libqios.a(qioseventdispatcher.o) + user_main_trampoline() in libqios.a(qioseventdispatcher.o) + +[QIOSApplicationStateTracker applicationWillTerminate] in libqios.a(qioseventdispatcher.o) + QIOSJumpingEventDispatcher::interruptEventLoopExec() in libqios.a(qioseventdispatcher.o) +ld: symbol(s) not found for architecture arm64 + +Change-Id: I43677cf3bce0588753731d81d533f85b0ea1e223 +Fixes: QTBUG-75457 +--- + src/corelib/kernel/qeventdispatcher_cf.mm | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git x/qtbase/src/corelib/kernel/qeventdispatcher_cf.mm y/qtbase/src/corelib/kernel/qeventdispatcher_cf.mm +index b7b379e2c1..ac711d74ee 100644 +--- x/qtbase/src/corelib/kernel/qeventdispatcher_cf.mm ++++ y/qtbase/src/corelib/kernel/qeventdispatcher_cf.mm +@@ -148,8 +148,8 @@ static CFStringRef runLoopMode(NSDictionary *dictionary) + + QT_BEGIN_NAMESPACE + +-Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher"); +-Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers"); ++Q_CORE_EXPORT Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher"); ++Q_CORE_EXPORT Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers"); + + class RunLoopDebugger : public QObject + { +-- +2.22.0 + diff --git a/patches/qt-Add-work-around-for-freebsd-build.patch b/libs/patches/qt-Add-work-around-for-freebsd-build.patch similarity index 84% rename from patches/qt-Add-work-around-for-freebsd-build.patch rename to libs/patches/qt-Add-work-around-for-freebsd-build.patch index 80c02d2..4ca4eef 100644 --- a/patches/qt-Add-work-around-for-freebsd-build.patch +++ b/libs/patches/qt-Add-work-around-for-freebsd-build.patch @@ -1,7 +1,7 @@ -From 9e482ce286ad39677e64392e0ca18afc4cf5396c Mon Sep 17 00:00:00 2001 +From c06988f6a5816e8ebbcebfcf7e9799cd5b90a91d Mon Sep 17 00:00:00 2001 From: Lars Schmertmann Date: Fri, 13 Apr 2018 08:22:18 +0200 -Subject: [PATCH] Add work-around for freebsd build +Subject: Add work-around for freebsd build Change-Id: I14e66e072f9667479815693e3dbbac71385797e7 Task-number: QTBUG-65425 @@ -10,7 +10,7 @@ Task-number: QTBUG-65425 1 file changed, 1 insertion(+), 1 deletion(-) diff --git x/qtbase/qmake/Makefile.unix y/qtbase/qmake/Makefile.unix -index 426387f0c2..b785127ed2 100644 +index 0f69b6b487..26ac96c473 100644 --- x/qtbase/qmake/Makefile.unix +++ y/qtbase/qmake/Makefile.unix @@ -269,7 +269,7 @@ qlibraryinfo.o: $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp @@ -23,5 +23,5 @@ index 426387f0c2..b785127ed2 100644 qnumeric.o: $(SOURCE_PATH)/src/corelib/global/qnumeric.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< -- -2.17.0 +2.21.0 diff --git a/libs/patches/qt-Adjust-iOS-target.patch b/libs/patches/qt-Adjust-iOS-target.patch new file mode 100644 index 0000000..cf9f193 --- /dev/null +++ b/libs/patches/qt-Adjust-iOS-target.patch @@ -0,0 +1,26 @@ +From 86b0caf2e167953ef1e50f32edb96e1e64eeba7e Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Fri, 19 Jul 2019 15:19:08 +0200 +Subject: Adjust iOS target + +Change-Id: I6c7f0616d52b0d118ffd8c031f3f51212b8ed821 +--- + mkspecs/macx-ios-clang/qmake.conf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git x/qtbase/mkspecs/macx-ios-clang/qmake.conf y/qtbase/mkspecs/macx-ios-clang/qmake.conf +index 88e96ef32e..b3372a8232 100644 +--- x/qtbase/mkspecs/macx-ios-clang/qmake.conf ++++ y/qtbase/mkspecs/macx-ios-clang/qmake.conf +@@ -2,7 +2,7 @@ + # qmake configuration for macx-ios-clang + # + +-QMAKE_IOS_DEPLOYMENT_TARGET = 11.0 ++QMAKE_IOS_DEPLOYMENT_TARGET = 13.0 + + # Universal target (iPhone and iPad) + QMAKE_APPLE_TARGETED_DEVICE_FAMILY = 1,2 +-- +2.22.0 + diff --git a/libs/patches/qt-Core-IO-Bluetooth-fix-ambiguous-conversions-for-macO.patch b/libs/patches/qt-Core-IO-Bluetooth-fix-ambiguous-conversions-for-macO.patch new file mode 100644 index 0000000..bd6bd8a --- /dev/null +++ b/libs/patches/qt-Core-IO-Bluetooth-fix-ambiguous-conversions-for-macO.patch @@ -0,0 +1,253 @@ +From daf0e91b5be5a136d93b0e66efc3681f28a2ffde Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= +Date: Fri, 19 Jul 2019 11:06:59 +0200 +Subject: [PATCH] Core/IO/Bluetooth - fix ambiguous conversions for macOS + +This is a sibling of QTBUG-76847 on macOS instead of iOS. +--- + .../qbluetoothdevicediscoveryagent_osx.mm | 6 ++--- + src/bluetooth/qbluetoothlocaldevice_osx.mm | 2 +- + src/bluetooth/qbluetoothserver_osx.mm | 6 ++--- + src/bluetooth/qbluetoothserviceinfo_osx.mm | 2 +- + src/bluetooth/qbluetoothsocket_osx.mm | 26 +++++++++---------- + src/bluetooth/qbluetoothtransferreply_osx.mm | 10 +++---- + 6 files changed, 26 insertions(+), 26 deletions(-) + +diff --git x/qtconnectivity/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm y/qtconnectivity/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm +index 4657da82..bdc3c85e 100644 +--- x/qtconnectivity/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm ++++ y/qtconnectivity/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm +@@ -181,7 +181,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con + + QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() + { +- if (inquiryLE && agentState != NonActive) { ++ if (inquiryLE.data() && agentState != NonActive) { + // We want the LE scan to stop as soon as possible. + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + // Local variable to be retained ... +@@ -195,7 +195,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() + + bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const + { +- return hostController && [hostController powerState] == kBluetoothHCIPowerStateON; ++ return hostController.data() && [hostController powerState] == kBluetoothHCIPowerStateON; + } + + bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const +@@ -292,7 +292,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLE() + + // Check queue and create scanner: + inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]); +- if (inquiryLE) ++ if (inquiryLE.data()) + notifier.take(); // Whatever happens next, inquiryLE is already the owner ... + + dispatch_queue_t leQueue(qt_LE_queue()); +diff --git x/qtconnectivity/src/bluetooth/qbluetoothlocaldevice_osx.mm y/qtconnectivity/src/bluetooth/qbluetoothlocaldevice_osx.mm +index 52b7bba8..e7dd9906 100644 +--- x/qtconnectivity/src/bluetooth/qbluetoothlocaldevice_osx.mm ++++ y/qtconnectivity/src/bluetooth/qbluetoothlocaldevice_osx.mm +@@ -149,7 +149,7 @@ QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice + + bool QBluetoothLocalDevicePrivate::isValid() const + { +- return hostController; ++ return hostController.data(); + } + + void QBluetoothLocalDevicePrivate::requestPairing(const QBluetoothAddress &address, Pairing pairing) +diff --git x/qtconnectivity/src/bluetooth/qbluetoothserver_osx.mm y/qtconnectivity/src/bluetooth/qbluetoothserver_osx.mm +index eefaf4da..5d3b8fc4 100644 +--- x/qtconnectivity/src/bluetooth/qbluetoothserver_osx.mm ++++ y/qtconnectivity/src/bluetooth/qbluetoothserver_osx.mm +@@ -142,7 +142,7 @@ void QBluetoothServerPrivate::stopListener() + + void QBluetoothServerPrivate::openNotify(IOBluetoothRFCOMMChannel *channel) + { +- Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)"); ++ Q_ASSERT_X(listener.data(), Q_FUNC_INFO, "invalid listener (nil)"); + Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)"); + Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); + +@@ -154,7 +154,7 @@ void QBluetoothServerPrivate::openNotify(IOBluetoothRFCOMMChannel *channel) + + void QBluetoothServerPrivate::openNotify(IOBluetoothL2CAPChannel *channel) + { +- Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)"); ++ Q_ASSERT_X(listener.data(), Q_FUNC_INFO, "invalid listener (nil)"); + Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)"); + Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); + +@@ -293,7 +293,7 @@ bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) + + OSXBluetooth::qt_test_iobluetooth_runloop(); + +- if (d_ptr->listener) { ++ if (d_ptr->listener.data()) { + qCWarning(QT_BT_OSX) << "already in listen mode, close server first"; + return false; + } +diff --git x/qtconnectivity/src/bluetooth/qbluetoothserviceinfo_osx.mm y/qtconnectivity/src/bluetooth/qbluetoothserviceinfo_osx.mm +index 34de4695..7ce4c645 100644 +--- x/qtconnectivity/src/bluetooth/qbluetoothserviceinfo_osx.mm ++++ y/qtconnectivity/src/bluetooth/qbluetoothserviceinfo_osx.mm +@@ -152,7 +152,7 @@ bool QBluetoothServiceInfoPrivate::unregisterService() + if (!registered) + return false; + +- Q_ASSERT_X(serviceRecord, Q_FUNC_INFO, "service registered, but serviceRecord is nil"); ++ Q_ASSERT_X(serviceRecord.data(), Q_FUNC_INFO, "service registered, but serviceRecord is nil"); + + [serviceRecord removeServiceRecord]; + serviceRecord.reset(nil); +diff --git x/qtconnectivity/src/bluetooth/qbluetoothsocket_osx.mm y/qtconnectivity/src/bluetooth/qbluetoothsocket_osx.mm +index 7f630146..2a856092 100644 +--- x/qtconnectivity/src/bluetooth/qbluetoothsocket_osx.mm ++++ y/qtconnectivity/src/bluetooth/qbluetoothsocket_osx.mm +@@ -101,13 +101,13 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, + + if (socketType == QBluetoothServiceInfo::RfcommProtocol) { + rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this]); +- if (rfcommChannel) ++ if (rfcommChannel.data()) + status = [rfcommChannel connectAsyncToDevice:address withChannelID:port]; + else + status = kIOReturnNoMemory; + } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { + l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this]); +- if (l2capChannel) ++ if (l2capChannel.data()) + status = [l2capChannel connectAsyncToDevice:address withPSM:port]; + else + status = kIOReturnNoMemory; +@@ -181,10 +181,10 @@ QString QBluetoothSocketPrivate::peerName() const + + NSString *nsName = nil; + if (socketType == QBluetoothServiceInfo::RfcommProtocol) { +- if (rfcommChannel) ++ if (rfcommChannel.data()) + nsName = [rfcommChannel peerName]; + } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { +- if (l2capChannel) ++ if (l2capChannel.data()) + nsName = [l2capChannel peerName]; + } + +@@ -198,10 +198,10 @@ QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const + { + BluetoothDeviceAddress addr = {}; + if (socketType == QBluetoothServiceInfo::RfcommProtocol) { +- if (rfcommChannel) ++ if (rfcommChannel.data()) + addr = [rfcommChannel peerAddress]; + } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { +- if (l2capChannel) ++ if (l2capChannel.data()) + addr = [l2capChannel peerAddress]; + } + +@@ -211,10 +211,10 @@ QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const + quint16 QBluetoothSocketPrivate::peerPort() const + { + if (socketType == QBluetoothServiceInfo::RfcommProtocol) { +- if (rfcommChannel) ++ if (rfcommChannel.data()) + return [rfcommChannel getChannelID]; + } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { +- if (l2capChannel) ++ if (l2capChannel.data()) + return [l2capChannel getPSM]; + } + +@@ -231,7 +231,7 @@ void QBluetoothSocketPrivate::_q_writeNotify() + Q_ASSERT_X(socketType == QBluetoothServiceInfo::L2capProtocol + || socketType == QBluetoothServiceInfo::RfcommProtocol, + Q_FUNC_INFO, "invalid socket type"); +- Q_ASSERT_X(l2capChannel || rfcommChannel, Q_FUNC_INFO, ++ Q_ASSERT_X(l2capChannel.data() || rfcommChannel.data(), Q_FUNC_INFO, + "invalid socket (no open channel)"); + Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); + +@@ -275,13 +275,13 @@ bool QBluetoothSocketPrivate::setChannel(IOBluetoothRFCOMMChannel *channel) + + openMode = QIODevice::ReadWrite; + rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this channel:channel]); +- if (rfcommChannel) {// We do not handle errors, up to an external user. ++ if (rfcommChannel.data()) {// We do not handle errors, up to an external user. + q_ptr->setOpenMode(QIODevice::ReadWrite); + state = QBluetoothSocket::ConnectedState; + socketType = QBluetoothServiceInfo::RfcommProtocol; + } + +- return rfcommChannel; ++ return rfcommChannel.data(); + } + + bool QBluetoothSocketPrivate::setChannel(IOBluetoothL2CAPChannel *channel) +@@ -299,13 +299,13 @@ bool QBluetoothSocketPrivate::setChannel(IOBluetoothL2CAPChannel *channel) + + openMode = QIODevice::ReadWrite; + l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this channel:channel]); +- if (l2capChannel) {// We do not handle errors, up to an external user. ++ if (l2capChannel.data()) {// We do not handle errors, up to an external user. + q_ptr->setOpenMode(QIODevice::ReadWrite); + state = QBluetoothSocket::ConnectedState; + socketType = QBluetoothServiceInfo::L2capProtocol; + } + +- return l2capChannel; ++ return l2capChannel.data(); + } + + +diff --git x/qtconnectivity/src/bluetooth/qbluetoothtransferreply_osx.mm y/qtconnectivity/src/bluetooth/qbluetoothtransferreply_osx.mm +index 65c8f82d..40a747f8 100644 +--- x/qtconnectivity/src/bluetooth/qbluetoothtransferreply_osx.mm ++++ y/qtconnectivity/src/bluetooth/qbluetoothtransferreply_osx.mm +@@ -136,13 +136,13 @@ QBluetoothTransferReplyOSXPrivate::~QBluetoothTransferReplyOSXPrivate() + // The OBEX session will be closed then. If + // somehow IOBluetooth/OBEX still has a reference to our + // session, it will not call any of delegate's callbacks. +- if (session) ++ if (session.data()) + [session closeSession]; + } + + bool QBluetoothTransferReplyOSXPrivate::isActive() const + { +- return agent || (session && [session hasActiveRequest]); ++ return agent.data() || (session.data() && [session hasActiveRequest]); + } + + bool QBluetoothTransferReplyOSXPrivate::startOPP(const QBluetoothAddress &device) +@@ -218,7 +218,7 @@ void QBluetoothTransferReplyOSXPrivate::sendConnect(const QBluetoothAddress &dev + void QBluetoothTransferReplyOSXPrivate::sendPut() + { + Q_ASSERT_X(inputStream, Q_FUNC_INFO, "invalid input stream (null)"); +- Q_ASSERT_X(session, Q_FUNC_INFO, "invalid OBEX session (nil)"); ++ Q_ASSERT_X(session.data(), Q_FUNC_INFO, "invalid OBEX session (nil)"); + Q_ASSERT_X([session isConnected], Q_FUNC_INFO, "not connected"); + Q_ASSERT_X(![session hasActiveRequest], Q_FUNC_INFO, + "session already has an active request"); +@@ -268,7 +268,7 @@ void QBluetoothTransferReplyOSXPrivate::OBEXConnectError(OBEXError errorCode, OB + Q_UNUSED(errorCode) + Q_UNUSED(response) + +- if (session) { ++ if (session.data()) { + setReplyError(QBluetoothTransferReply::SessionError, + QCoreApplication::translate(TRANSFER_REPLY, TR_CONNECT_FAILED)); + } else { +@@ -283,7 +283,7 @@ void QBluetoothTransferReplyOSXPrivate::OBEXConnectError(OBEXError errorCode, OB + void QBluetoothTransferReplyOSXPrivate::OBEXConnectSuccess() + { + // Now that OBEX connect succeeded, we can send an OBEX put request. +- if (!session) { ++ if (!session.data()) { + // We're still in OBEXConnect(), it'll take care of next steps. + return; + } +-- +2.22.0 + diff --git a/libs/patches/qt-Core-IO-Bluetooth-fix-ambiguous-conversions.patch b/libs/patches/qt-Core-IO-Bluetooth-fix-ambiguous-conversions.patch new file mode 100644 index 0000000..a2279c7 --- /dev/null +++ b/libs/patches/qt-Core-IO-Bluetooth-fix-ambiguous-conversions.patch @@ -0,0 +1,97 @@ +From cd287186955f1c654a1f35197f0515ffc222e734 Mon Sep 17 00:00:00 2001 +From: Timur Pocheptsov +Date: Wed, 3 Jul 2019 15:26:31 +0200 +Subject: [PATCH] Core/IO/Bluetooth - fix ambiguous conversions + +... somewhat prospective fix (I do not have the new iOS yet), so far build +never failed with my current SDK. + +Fixes: QTBUG-76847 +Change-Id: Iab75c3cd47144cd83b679b1dbf82339e29c07bd1 +--- + src/bluetooth/osx/osxbtperipheralmanager.mm | 4 ++-- + src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm | 6 +++--- + src/bluetooth/qlowenergycontroller_osx.mm | 6 +++--- + 3 files changed, 8 insertions(+), 8 deletions(-) + +diff --git x/qtconnectivity/src/bluetooth/osx/osxbtperipheralmanager.mm y/qtconnectivity/src/bluetooth/osx/osxbtperipheralmanager.mm +index 1998340a..39f9808c 100644 +--- x/qtconnectivity/src/bluetooth/osx/osxbtperipheralmanager.mm ++++ y/qtconnectivity/src/bluetooth/osx/osxbtperipheralmanager.mm +@@ -340,7 +340,7 @@ bool qt_validate_value_range(const QLowEnergyCharacteristicData &data) + - (void)startAdvertising + { + state = PeripheralState::waitingForPowerOn; +- if (manager) ++ if (manager.data()) + [manager setDelegate:nil]; + manager.reset([[CBPeripheralManager alloc] initWithDelegate:self + queue:OSXBluetooth::qt_LE_queue()]); +@@ -405,7 +405,7 @@ bool qt_validate_value_range(const QLowEnergyCharacteristicData &data) + + - (void) addServicesToPeripheral + { +- Q_ASSERT(manager); ++ Q_ASSERT(manager.data()); + + if (nextServiceToAdd < services.size()) + [manager addService:services[nextServiceToAdd++]]; +diff --git x/qtconnectivity/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm y/qtconnectivity/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm +index c50d546d..557785b4 100644 +--- x/qtconnectivity/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm ++++ y/qtconnectivity/src/bluetooth/qbluetoothdevicediscoveryagent_ios.mm +@@ -132,7 +132,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(con + + QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() + { +- if (inquiryLE) { ++ if (inquiryLE.data()) { + // We want the LE scan to stop as soon as possible. + if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) { + // Local variable to be retained ... +@@ -151,7 +151,7 @@ bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const + if (stopPending) + return false; + +- return inquiryLE; ++ return !!inquiryLE.data(); + } + + void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods /*methods*/) +@@ -178,7 +178,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent + this, &QBluetoothDeviceDiscoveryAgentPrivate::LEdeviceFound); + + inquiryLE.reset([[LEDeviceInquiryObjC alloc] initWithNotifier:notifier.data()]); +- if (inquiryLE) ++ if (inquiryLE.data()) + notifier.take(); // Whatever happens next, inquiryLE is already the owner ... + + dispatch_queue_t leQueue(qt_LE_queue()); +diff --git x/qtconnectivity/src/bluetooth/qlowenergycontroller_osx.mm y/qtconnectivity/src/bluetooth/qlowenergycontroller_osx.mm +index 8bcdc22e..9aaee855 100644 +--- x/qtconnectivity/src/bluetooth/qlowenergycontroller_osx.mm ++++ y/qtconnectivity/src/bluetooth/qlowenergycontroller_osx.mm +@@ -165,7 +165,7 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl + #endif + } else { + centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()]); +- if (!centralManager) { ++ if (!centralManager.data()) { + qCWarning(QT_BT_OSX) << "failed to initialize central manager"; + return; + } +@@ -200,9 +200,9 @@ QLowEnergyControllerPrivateOSX::~QLowEnergyControllerPrivateOSX() + bool QLowEnergyControllerPrivateOSX::isValid() const + { + #ifdef Q_OS_TVOS +- return centralManager; ++ return centralManager.data(); + #else +- return centralManager || peripheralManager; ++ return centralManager.data() || peripheralManager.data(); + #endif + } + +-- +2.22.0 + diff --git a/libs/patches/qt-Disable-qmltime-for-shared-build.patch b/libs/patches/qt-Disable-qmltime-for-shared-build.patch new file mode 100644 index 0000000..cd2f6ae --- /dev/null +++ b/libs/patches/qt-Disable-qmltime-for-shared-build.patch @@ -0,0 +1,32 @@ +From 9040ffde6a172f634249af58cd7f1bd3fcc33a00 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= +Date: Tue, 30 Jul 2019 10:42:57 +0200 +Subject: [PATCH] Disable qmltime for shared build + +=== BUILD TARGET qmltime OF PROJECT qmltime WITH CONFIGURATION Release === +Code Signing Error: Signing for "qmltime" requires a development team. Select a development team in the Signing & Capabilities editor. +Code Signing Error: Code signing is required for product type 'Application' in SDK 'iOS 13.0' +** BUILD FAILED ** + +Fixes: QTBUG-67135 +--- + tools/tools.pro | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git x/qtdeclarative/tools/tools.pro y/qtdeclarative/tools/tools.pro +index d3ec38071..d431ee835 100644 +--- x/qtdeclarative/tools/tools.pro ++++ y/qtdeclarative/tools/tools.pro +@@ -20,8 +20,7 @@ qtConfig(thread):!android|android_app { + qtHaveModule(quick) { + !static: { + SUBDIRS += \ +- qmlscene \ +- qmltime ++ qmlscene + + qtConfig(regularexpression):qtConfig(process) { + SUBDIRS += \ +-- +2.22.0 + diff --git a/patches/qt-Disable-unused-imageformats.patch b/libs/patches/qt-Disable-unused-imageformats.patch similarity index 60% rename from patches/qt-Disable-unused-imageformats.patch rename to libs/patches/qt-Disable-unused-imageformats.patch index 4b88c99..24dae83 100644 --- a/patches/qt-Disable-unused-imageformats.patch +++ b/libs/patches/qt-Disable-unused-imageformats.patch @@ -1,17 +1,18 @@ -From e1c05843ae1609075807d5a789fc4e6cd8154520 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= -Date: Mon, 25 Sep 2017 14:10:56 +0200 -Subject: [PATCH] Disable unused imageformats +From 2674afcd19c8ce6a23a3c69bb9d41d82838adf86 Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Tue, 18 Jun 2019 07:47:39 +0200 +Subject: Disable unused imageformats +Change-Id: Iace4d751b615d1e54d94e9f8ab774ef39b111a79 --- src/plugins/imageformats/imageformats.pro | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git x/qtimageformats/src/plugins/imageformats/imageformats.pro y/qtimageformats/src/plugins/imageformats/imageformats.pro -index d6c59ee..f1fb6d0 100644 +index be1e20a..06feab3 100644 --- x/qtimageformats/src/plugins/imageformats/imageformats.pro +++ y/qtimageformats/src/plugins/imageformats/imageformats.pro -@@ -18,8 +18,7 @@ config_jasper { +@@ -22,8 +22,7 @@ qtConfig(jasper) { SUBDIRS += macjp2 } @@ -22,5 +23,5 @@ index d6c59ee..f1fb6d0 100644 webp -} -- -2.16.2 +2.21.0 diff --git a/patches/qt-Enable-debug-output-for-OpenSSL.patch b/libs/patches/qt-Enable-debug-output-for-OpenSSL.patch similarity index 100% rename from patches/qt-Enable-debug-output-for-OpenSSL.patch rename to libs/patches/qt-Enable-debug-output-for-OpenSSL.patch diff --git a/libs/patches/qt-Fix-build-with-no-feature-printer-on-macOS.patch b/libs/patches/qt-Fix-build-with-no-feature-printer-on-macOS.patch new file mode 100644 index 0000000..bfe757e --- /dev/null +++ b/libs/patches/qt-Fix-build-with-no-feature-printer-on-macOS.patch @@ -0,0 +1,159 @@ +From fdb01d0c01261008f9055339db55c6e9693f18c8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= +Date: Tue, 23 Jul 2019 12:44:45 +0200 +Subject: [PATCH 2/2] Fix build with -no-feature-printer on macOS + +Fixes: QTBUG-62675 +Change-Id: I3bfcd6d78c3124769ff8662941472333c795fdbe +--- + src/plugins/platforms/cocoa/cocoa.pro | 42 +++++++++++-------- + .../platforms/cocoa/qcocoanativeinterface.mm | 6 +-- + .../platforms/cocoa/qpaintengine_mac.mm | 4 ++ + 3 files changed, 32 insertions(+), 20 deletions(-) + +diff --git x/qtbase/src/plugins/platforms/cocoa/cocoa.pro y/qtbase/src/plugins/platforms/cocoa/cocoa.pro +index 083b7c1655..4249bae2b1 100644 +--- x/qtbase/src/plugins/platforms/cocoa/cocoa.pro ++++ y/qtbase/src/plugins/platforms/cocoa/cocoa.pro +@@ -20,8 +20,6 @@ SOURCES += main.mm \ + qcocoamenuloader.mm \ + qcocoahelpers.mm \ + qmultitouch_mac.mm \ +- qcocoaaccessibilityelement.mm \ +- qcocoaaccessibility.mm \ + qcocoacursor.mm \ + qcocoaclipboard.mm \ + qcocoadrag.mm \ +@@ -55,8 +53,6 @@ HEADERS += qcocoaintegration.h \ + qcocoamenuloader.h \ + qcocoahelpers.h \ + qmultitouch_mac_p.h \ +- qcocoaaccessibilityelement.h \ +- qcocoaaccessibility.h \ + qcocoacursor.h \ + qcocoaclipboard.h \ + qcocoadrag.h \ +@@ -81,15 +77,24 @@ qtConfig(vulkan) { + HEADERS += qcocoavulkaninstance.h + } + ++qtConfig(accessibility) { ++ SOURCES += qcocoaaccessibilityelement.mm \ ++ qcocoaaccessibility.mm ++ HEADERS += qcocoaaccessibilityelement.h \ ++ qcocoaaccessibility.h ++} ++ + RESOURCES += qcocoaresources.qrc + + LIBS += -framework AppKit -framework CoreServices -framework Carbon -framework IOKit -framework QuartzCore -framework CoreVideo -framework Metal -framework IOSurface -lcups + + QT += \ + core-private gui-private \ +- accessibility_support-private clipboard_support-private theme_support-private \ ++ clipboard_support-private theme_support-private \ + fontdatabase_support-private graphics_support-private + ++qtConfig(accessibility): QT += accessibility_support-private ++ + qtConfig(vulkan): QT += vulkan_support-private + + CONFIG += no_app_extension_api_only +@@ -97,17 +102,19 @@ CONFIG += no_app_extension_api_only + qtHaveModule(widgets) { + QT_FOR_CONFIG += widgets + +- SOURCES += \ +- qpaintengine_mac.mm \ +- qprintengine_mac.mm \ +- qcocoaprintersupport.mm \ +- qcocoaprintdevice.mm \ +- +- HEADERS += \ +- qpaintengine_mac_p.h \ +- qprintengine_mac_p.h \ +- qcocoaprintersupport.h \ +- qcocoaprintdevice.h \ ++ SOURCES += qpaintengine_mac.mm ++ HEADERS += qpaintengine_mac_p.h ++ ++ qtHaveModule(printsupport) { ++ SOURCES += \ ++ qprintengine_mac.mm \ ++ qcocoaprintersupport.mm \ ++ qcocoaprintdevice.mm ++ HEADERS += \ ++ qcocoaprintersupport.h \ ++ qcocoaprintdevice.h \ ++ qprintengine_mac_p.h ++ } + + qtConfig(colordialog) { + SOURCES += qcocoacolordialoghelper.mm +@@ -124,7 +131,8 @@ qtHaveModule(widgets) { + HEADERS += qcocoafontdialoghelper.h + } + +- QT += widgets-private printsupport-private ++ QT += widgets-private ++ qtHaveModule(printsupport): QT += printsupport-private + } + + OTHER_FILES += cocoa.json +diff --git x/qtbase/src/plugins/platforms/cocoa/qcocoanativeinterface.mm y/qtbase/src/plugins/platforms/cocoa/qcocoanativeinterface.mm +index 7979e430ac..8c0bd1d158 100644 +--- x/qtbase/src/plugins/platforms/cocoa/qcocoanativeinterface.mm ++++ y/qtbase/src/plugins/platforms/cocoa/qcocoanativeinterface.mm +@@ -59,7 +59,7 @@ + #include "qguiapplication.h" + #include + +-#ifndef QT_NO_WIDGETS ++#if !defined(QT_NO_WIDGETS) && defined(QT_PRINTSUPPORT_LIB) + #include "qcocoaprintersupport.h" + #include "qprintengine_mac_p.h" + #include +@@ -153,7 +153,7 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QCocoaNativeInter + + QPlatformPrinterSupport *QCocoaNativeInterface::createPlatformPrinterSupport() + { +-#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) ++#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) && defined(QT_PRINTSUPPORT_LIB) + return new QCocoaPrinterSupport(); + #else + qFatal("Printing is not supported when Qt is configured with -no-widgets"); +@@ -163,7 +163,7 @@ QPlatformPrinterSupport *QCocoaNativeInterface::createPlatformPrinterSupport() + + void *QCocoaNativeInterface::NSPrintInfoForPrintEngine(QPrintEngine *printEngine) + { +-#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) ++#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) && defined(QT_PRINTSUPPORT_LIB) + QMacPrintEnginePrivate *macPrintEnginePriv = static_cast(printEngine)->d_func(); + if (macPrintEnginePriv->state == QPrinter::Idle && !macPrintEnginePriv->isPrintSessionInitialized()) + macPrintEnginePriv->initialize(); +diff --git x/qtbase/src/plugins/platforms/cocoa/qpaintengine_mac.mm y/qtbase/src/plugins/platforms/cocoa/qpaintengine_mac.mm +index 3677877538..00b2267f0d 100644 +--- x/qtbase/src/plugins/platforms/cocoa/qpaintengine_mac.mm ++++ y/qtbase/src/plugins/platforms/cocoa/qpaintengine_mac.mm +@@ -38,14 +38,18 @@ + ****************************************************************************/ + + #include "qpaintengine_mac_p.h" ++#if defined(QT_PRINTSUPPORT_LIB) + #include "qprintengine_mac_p.h" ++#endif + + #include + #include + #include + #include + #include ++#if defined(QT_PRINTSUPPORT_LIB) + #include ++#endif + #include + #include + #include +-- +2.22.0 + diff --git a/libs/patches/qt-Fix-build-with-no-feature-printer.patch b/libs/patches/qt-Fix-build-with-no-feature-printer.patch new file mode 100644 index 0000000..1bb8cfa --- /dev/null +++ b/libs/patches/qt-Fix-build-with-no-feature-printer.patch @@ -0,0 +1,54 @@ +From d8ac3ad8e4dfc26c6b2f4bb696d4ec8cc23d8cbf Mon Sep 17 00:00:00 2001 +From: Kai Koehne +Date: Tue, 9 Jul 2019 17:19:26 +0200 +Subject: [PATCH 1/2] Fix build with -no-feature-printer +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Skip printsupport subdirectory if printer feature is disabled. Also +removed android-embedded condition for the plugin: Such a +configuration should just disable the printer feature. + +Fixes: QTBUG-76941 +Change-Id: Ifca7d2311a575c1589ad6a87a775bd016591ee2c +Reviewed-by: Jörg Bornemann +--- + src/src.pro | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git x/qtbase/src/src.pro y/qtbase/src/src.pro +index 1c76a2e46f..b704ccd7ab 100644 +--- x/qtbase/src/src.pro ++++ y/qtbase/src/src.pro +@@ -1,8 +1,10 @@ + TEMPLATE = subdirs + +-QT_FOR_CONFIG += core-private gui-private ++QT_FOR_CONFIG += core-private gui-private printsupport ++ + include($$OUT_PWD/corelib/qtcore-config.pri) + include($$OUT_PWD/gui/qtgui-config.pri) ++include($$OUT_PWD/printsupport/qtprintsupport-config.pri) + + force_bootstrap|!qtConfig(commandlineparser): \ + CONFIG += force_dbus_bootstrap +@@ -221,11 +223,13 @@ qtConfig(gui) { + src_testlib.depends += src_gui # if QtGui is enabled, QtTest requires QtGui's headers + qtConfig(widgets) { + SUBDIRS += src_tools_uic src_widgets +- !android-embedded: SUBDIRS += src_printsupport + TOOLS += src_tools_uic + src_plugins.depends += src_widgets +- !android-embedded: src_plugins.depends += src_printsupport + src_testlib.depends += src_widgets # if QtWidgets is enabled, QtTest requires QtWidgets's headers ++ qtConfig(printer) { ++ SUBDIRS += src_printsupport ++ src_plugins.depends += src_printsupport ++ } + qtConfig(opengl) { + SUBDIRS += src_opengl + src_plugins.depends += src_opengl +-- +2.22.0 + diff --git a/libs/patches/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch b/libs/patches/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch new file mode 100644 index 0000000..1c35598 --- /dev/null +++ b/libs/patches/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch @@ -0,0 +1,25 @@ +From c69fef8cde15345cb53748456a2e7136258f42a4 Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Mon, 16 Apr 2018 08:34:42 +0200 +Subject: Remove Qt Labs specific plugins from the build + +Change-Id: I8f1444d4be2806e983979ceff73223c7452da5c2 +--- + src/imports/imports.pro | 1 - + 1 file changed, 1 deletion(-) + +diff --git x/qtquickcontrols2/src/imports/imports.pro y/qtquickcontrols2/src/imports/imports.pro +index e32bded7..2345616e 100644 +--- x/qtquickcontrols2/src/imports/imports.pro ++++ y/qtquickcontrols2/src/imports/imports.pro +@@ -1,7 +1,6 @@ + TEMPLATE = subdirs + SUBDIRS += \ + controls \ +- calendar \ + platform \ + templates + +-- +2.21.0 + diff --git a/libs/patches/qt-Remove-unused-plugins-from-the-build.patch b/libs/patches/qt-Remove-unused-plugins-from-the-build.patch new file mode 100644 index 0000000..b7495fa --- /dev/null +++ b/libs/patches/qt-Remove-unused-plugins-from-the-build.patch @@ -0,0 +1,37 @@ +From 0aa601d4cfdded1bd1c9924244795c4bb3d1b227 Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Mon, 16 Apr 2018 08:53:54 +0200 +Subject: Remove unused plugins from the build + +Change-Id: I780168aa4481c4bc6c9570effd80d34ce097d08a +--- + src/imports/imports.pro | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git x/qtdeclarative/src/imports/imports.pro y/qtdeclarative/src/imports/imports.pro +index 24e93fec1..aced2e417 100644 +--- x/qtdeclarative/src/imports/imports.pro ++++ y/qtdeclarative/src/imports/imports.pro +@@ -3,10 +3,8 @@ TEMPLATE = subdirs + SUBDIRS += \ + builtins \ + qtqml \ +- models \ +- labsmodels ++ models + +-qtConfig(thread): SUBDIRS += folderlistmodel + qtHaveModule(sql): SUBDIRS += localstorage + qtConfig(settings): SUBDIRS += settings + qtConfig(statemachine): SUBDIRS += statemachine +@@ -21,7 +19,6 @@ qtHaveModule(quick) { + wavefrontmesh + + qtHaveModule(testlib): SUBDIRS += testlib +- qtConfig(systemsemaphore): SUBDIRS += sharedimage + qtConfig(quick-particles): \ + SUBDIRS += particles + +-- +2.21.0 + diff --git a/libs/patches/qt-disable-designer.patch b/libs/patches/qt-disable-designer.patch new file mode 100644 index 0000000..dcec905 --- /dev/null +++ b/libs/patches/qt-disable-designer.patch @@ -0,0 +1,52 @@ +From 0b9dc418d21a54b748637f4853dbe340c2dbc33e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= +Date: Tue, 16 Oct 2018 17:55:29 +0200 +Subject: [PATCH] disable designer + +--- + src/linguist/linguist.pro | 9 --------- + src/src.pro | 7 ------- + 2 files changed, 16 deletions(-) + +diff --git x/qttools/src/linguist/linguist.pro y/qttools/src/linguist/linguist.pro +index 3f54c396..61903c08 100644 +--- x/qttools/src/linguist/linguist.pro ++++ y/qttools/src/linguist/linguist.pro +@@ -3,16 +3,7 @@ SUBDIRS = \ + lrelease \ + lupdate \ + lconvert +-!no-png:qtHaveModule(widgets) { +- QT_FOR_CONFIG += widgets +- qtConfig(process):qtConfig(pushbutton):qtConfig(toolbutton) { +- SUBDIRS += linguist +- } +-} + +-qtNomakeTools( \ +- linguist \ +-) + + equals(QMAKE_HOST.os, Windows): CMAKE_BIN_SUFFIX = ".exe" + +diff --git x/qttools/src/src.pro y/qttools/src/src.pro +index 7a1af007..a9d4ac4b 100644 +--- x/qttools/src/src.pro ++++ y/qttools/src/src.pro +@@ -5,13 +5,6 @@ qtHaveModule(widgets) { + message("Some graphics-related tools are unavailable without PNG support") + } else { + QT_FOR_CONFIG += widgets +- qtConfig(pushbutton):qtConfig(toolbutton) { +- SUBDIRS = assistant \ +- designer \ +- pixeltool +- +- linguist.depends = designer +- } + qtHaveModule(quick):qtConfig(thread):qtConfig(toolbutton): SUBDIRS += distancefieldgenerator + } + } +-- +2.19.1 + diff --git a/libs/qt-install.qs b/libs/qt-install.qs new file mode 100644 index 0000000..bd00e2b --- /dev/null +++ b/libs/qt-install.qs @@ -0,0 +1,98 @@ +function Controller() +{ + installer.setMessageBoxAutomaticAnswer("OverwriteTargetDirectory", QMessageBox.Yes); + installer.setMessageBoxAutomaticAnswer("TargetDirectoryInUse", QMessageBox.Ok); + installer.setMessageBoxAutomaticAnswer("cancelInstallation", QMessageBox.Yes); +} + +Controller.prototype.WelcomePageCallback = function() +{ + console.log("Welcome"); + var widget = gui.currentPageWidget(); + gui.clickButton(buttons.NextButton); + widget.completeChanged.connect(function() + { + gui.clickButton(buttons.NextButton); + }); +} + +Controller.prototype.CredentialsPageCallback = function() +{ + console.log("Credentials"); + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.IntroductionPageCallback = function() +{ + console.log("Introduction"); + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.TargetDirectoryPageCallback = function() +{ + console.log("TargetDirectory: " + installer.value("TargetDir")); + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.ComponentSelectionPageCallback = function() +{ + var packages = installer.value("Packages") + console.log("ComponentSelection: " + packages); + var widget = gui.currentPageWidget(); + widget.deselectAll(); + + packages = packages.split(","); + var components = installer.components(); + for (var i in packages) + { + pkg = packages[i]; + for (var j in components) + { + if (components[j].name === pkg) + { + widget.selectComponent(pkg); + break; + } + } + } + + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.LicenseAgreementPageCallback = function() +{ + console.log("LicenseAgreement"); + var widget = gui.currentPageWidget(); + widget.AcceptLicenseRadioButton.setChecked(true); + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.ReadyForInstallationPageCallback = function() +{ + console.log("ReadyForInstallation"); + gui.clickButton(buttons.CommitButton); +} + +Controller.prototype.PerformInstallationPageCallback = function() +{ + console.log("PerformInstallation"); + installer.installationFinished.connect(function() + { + gui.clickButton(buttons.NextButton); + }); +} + +Controller.prototype.FinishedPageCallback = function() +{ + console.log("Finished"); + var widget = gui.currentPageWidget(); + if (widget.LaunchQtCreatorCheckBoxForm) + { + widget.LaunchQtCreatorCheckBoxForm.launchQtCreatorCheckBox.setChecked(false); + } + else if (widget.RunItCheckBox) + { + widget.RunItCheckBox.setChecked(false); + } + gui.clickButton(buttons.FinishButton); +} diff --git a/libs/qt.cmake b/libs/qt.cmake new file mode 100644 index 0000000..e65cc78 --- /dev/null +++ b/libs/qt.cmake @@ -0,0 +1,95 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.11.0) + +########################################### +# Usage: cmake -DVERSION=5.10.0 -P qt.cmake +########################################### + +IF(NOT PACKAGES_DIR) + SET(PACKAGES_DIR $ENV{PACKAGES_DIR}) + IF(NOT PACKAGES_DIR) + SET(PACKAGES_DIR ${CMAKE_BINARY_DIR}) + ENDIF() +ENDIF() +MESSAGE(STATUS "Use PACKAGES_DIR: ${PACKAGES_DIR}") + +IF(NOT VERSION) + MESSAGE(FATAL_ERROR "Please provide Qt version (-DVERSION=5.10.0)") +ENDIF() + +FUNCTION(READ_FILE _filename _regex _out) + FILE(STRINGS "${CMAKE_BINARY_DIR}/${_filename}" content REGEX "${_regex}") + STRING(REGEX MATCH "${_regex}" _unused "${content}") + SET(${_out} ${CMAKE_MATCH_1} PARENT_SCOPE) +ENDFUNCTION() + +FUNCTION(FETCH_XML _url _out_url) + SET(_filename Updates.xml) + FILE(DOWNLOAD "${_url}/${_filename}" "${CMAKE_BINARY_DIR}/${_filename}") + + READ_FILE("${_filename}" "(.+)" archive) + READ_FILE("${_filename}" "(.+)" name) + READ_FILE("${_filename}" "([-|\.|0-9]+)<\/Version>" version) + + SET(${_out_url} "${_url}/${name}/${version}${archive}" PARENT_SCOPE) +ENDFUNCTION() + +FUNCTION(FETCH_HASH _url _hash_algo _out_hash) + STRING(TOLOWER "${_hash_algo}" suffix) + + GET_FILENAME_COMPONENT(filename "${_url}" NAME) + FILE(DOWNLOAD "${_url}.${suffix}" "${CMAKE_BINARY_DIR}/${filename}.${suffix}") + FILE(STRINGS ${CMAKE_BINARY_DIR}/${filename}.${suffix} content) + STRING(REGEX MATCH "^[a-z|0-9]+" hash "${content}") + + IF(NOT hash) + MESSAGE(FATAL_ERROR "Cannot fetch hash: ${_url}.${suffix}") + ENDIF() + SET(${_out_hash} ${hash} PARENT_SCOPE) +ENDFUNCTION() + +INCLUDE(FetchContent) +SET(FETCHCONTENT_QUIET FALSE) +SET(HASH_ALGO SHA256) +SET(QT_SDK_URL https://download.qt.io/online/qtsdkrepository) + + + +############################ OpenSSL +SET(OPENSSL_URL ${QT_SDK_URL}/linux_x64/desktop/tools_openssl_x64) +FETCH_XML("${OPENSSL_URL}" OPENSSL_URL) +FETCH_HASH("${OPENSSL_URL}" ${HASH_ALGO} OPENSSL_HASH) + +FetchContent_Populate(openssl + URL ${OPENSSL_URL} + URL_HASH SHA256=${OPENSSL_HASH} + DOWNLOAD_DIR ${PACKAGES_DIR} +) +FetchContent_GetProperties(openssl) + +FILE(COPY "${openssl_SOURCE_DIR}/OpenSSL/binary/" DESTINATION b/${VERSION}/gcc_64) + + + +############################ Qt +STRING(SUBSTRING ${VERSION} 0 4 SUBVERSION) +SET(QT_FILE qt-opensource-linux-x64-${VERSION}.run) +SET(QT_URL https://download.qt.io/archive/qt/${SUBVERSION}/${VERSION}/${QT_FILE}) + +FETCH_HASH("${QT_URL}" ${HASH_ALGO} QT_HASH) + +FetchContent_Populate(qt + URL ${QT_URL} + URL_HASH SHA256=${QT_HASH} + DOWNLOAD_DIR ${PACKAGES_DIR} + DOWNLOAD_NO_EXTRACT TRUE +) + + +SET(ENV{XDG_DATA_HOME} ${CMAKE_BINARY_DIR}) +SET(ENV{XDG_DATA_DIRS} ${CMAKE_BINARY_DIR}) +SET(ENV{HOME} ${CMAKE_BINARY_DIR}) +STRING(REPLACE "." "" PKGVERSION "${VERSION}") +GET_FILENAME_COMPONENT(source_dir "${CMAKE_SCRIPT_MODE_FILE}" DIRECTORY) +EXECUTE_PROCESS(COMMAND chmod +x ${PACKAGES_DIR}/${QT_FILE}) +EXECUTE_PROCESS(COMMAND ${PACKAGES_DIR}/${QT_FILE} --script ${source_dir}/qt-install.qs -v --platform minimal TargetDir=b Packages=qt.qt5.${PKGVERSION}.gcc_64) +EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E create_symlink b/${VERSION}/gcc_64 dist) diff --git a/patches/openssl-DSA-mod-inverse-fix.patch b/patches/openssl-DSA-mod-inverse-fix.patch deleted file mode 100644 index b1e511a..0000000 --- a/patches/openssl-DSA-mod-inverse-fix.patch +++ /dev/null @@ -1,78 +0,0 @@ -From f1b12b8713a739f27d74e6911580b2e70aea2fa4 Mon Sep 17 00:00:00 2001 -From: Pauli -Date: Mon, 29 Oct 2018 06:50:51 +1000 -Subject: [PATCH 1/3] DSA mod inverse fix - -There is a side channel attack against the division used to calculate one of -the modulo inverses in the DSA algorithm. This change takes advantage of the -primality of the modulo and Fermat's little theorem to calculate the inverse -without leaking information. - -Thanks to Samuel Weiser for finding and reporting this. - -Reviewed-by: Matthias St. Pierre -Reviewed-by: Bernd Edlinger -(Merged from https://github.com/openssl/openssl/pull/7487) - -(cherry picked from commit 415c33563528667868c3c653a612e6fc8736fd79) ---- - crypto/dsa/dsa_ossl.c | 32 +++++++++++++++++++++++++++++++- - 1 file changed, 31 insertions(+), 1 deletion(-) - -diff --git x/crypto/dsa/dsa_ossl.c y/crypto/dsa/dsa_ossl.c -index ac1f65a51a..ca20811200 100644 ---- x/crypto/dsa/dsa_ossl.c -+++ y/crypto/dsa/dsa_ossl.c -@@ -23,6 +23,8 @@ static int dsa_do_verify(const unsigned char *dgst, int dgst_len, - DSA_SIG *sig, DSA *dsa); - static int dsa_init(DSA *dsa); - static int dsa_finish(DSA *dsa); -+static BIGNUM *dsa_mod_inverse_fermat(const BIGNUM *k, const BIGNUM *q, -+ BN_CTX *ctx); - - static DSA_METHOD openssl_dsa_meth = { - "OpenSSL DSA method", -@@ -259,7 +261,7 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, - goto err; - - /* Compute part of 's = inv(k) (m + xr) mod q' */ -- if ((kinv = BN_mod_inverse(NULL, k, dsa->q, ctx)) == NULL) -+ if ((kinv = dsa_mod_inverse_fermat(k, dsa->q, ctx)) == NULL) - goto err; - - BN_clear_free(*kinvp); -@@ -393,3 +395,31 @@ static int dsa_finish(DSA *dsa) - BN_MONT_CTX_free(dsa->method_mont_p); - return 1; - } -+ -+/* -+ * Compute the inverse of k modulo q. -+ * Since q is prime, Fermat's Little Theorem applies, which reduces this to -+ * mod-exp operation. Both the exponent and modulus are public information -+ * so a mod-exp that doesn't leak the base is sufficient. A newly allocated -+ * BIGNUM is returned which the caller must free. -+ */ -+static BIGNUM *dsa_mod_inverse_fermat(const BIGNUM *k, const BIGNUM *q, -+ BN_CTX *ctx) -+{ -+ BIGNUM *res = NULL; -+ BIGNUM *r, *e; -+ -+ if ((r = BN_new()) == NULL) -+ return NULL; -+ -+ BN_CTX_start(ctx); -+ if ((e = BN_CTX_get(ctx)) != NULL -+ && BN_set_word(r, 2) -+ && BN_sub(e, q, r) -+ && BN_mod_exp_mont(r, k, e, q, ctx, NULL)) -+ res = r; -+ else -+ BN_free(r); -+ BN_CTX_end(ctx); -+ return res; -+} --- -2.19.1 - diff --git a/patches/openssl-Ignore-disabled-ciphers.patch b/patches/openssl-Ignore-disabled-ciphers.patch deleted file mode 100644 index aab52cf..0000000 --- a/patches/openssl-Ignore-disabled-ciphers.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 2bcd8e6e1fe62ef5667c1bb8ad1bfe54a0aeaa99 Mon Sep 17 00:00:00 2001 -From: Matt Caswell -Date: Wed, 24 Oct 2018 10:11:00 +0100 -Subject: [PATCH] Ignore disabled ciphers when deciding if we are using ECC - -use_ecc() was always returning 1 because there are default (TLSv1.3) -ciphersuites that use ECC - even if those ciphersuites are disabled by -other options. - -Fixes #7471 ---- - ssl/statem/extensions_clnt.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git x/ssl/statem/extensions_clnt.c y/ssl/statem/extensions_clnt.c -index 4b5e6fe2b87..dcadc2a3231 100644 ---- x/ssl/statem/extensions_clnt.c -+++ y/ssl/statem/extensions_clnt.c -@@ -128,6 +128,10 @@ static int use_ecc(SSL *s) - for (i = 0; i < end; i++) { - const SSL_CIPHER *c = sk_SSL_CIPHER_value(cipher_stack, i); - -+ /* Skip disabled ciphers */ -+ if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_SUPPORTED, 0)) -+ continue; -+ - alg_k = c->algorithm_mkey; - alg_a = c->algorithm_auth; - if ((alg_k & (SSL_kECDHE | SSL_kECDHEPSK)) - diff --git a/patches/openssl-Timing-vulnerability-in-DSA-signature-generation-CVE.patch b/patches/openssl-Timing-vulnerability-in-DSA-signature-generation-CVE.patch deleted file mode 100644 index a5d7611..0000000 --- a/patches/openssl-Timing-vulnerability-in-DSA-signature-generation-CVE.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 8abfe72e8c1de1b95f50aa0d9134803b4d00070f Mon Sep 17 00:00:00 2001 -From: Pauli -Date: Wed, 24 Oct 2018 07:42:46 +1000 -Subject: [PATCH 2/3] Timing vulnerability in DSA signature generation - (CVE-2018-0734). - -Avoid a timing attack that leaks information via a side channel that -triggers when a BN is resized. Increasing the size of the BNs -prior to doing anything with them suppresses the attack. - -Thanks due to Samuel Weiser for finding and locating this. - -Reviewed-by: Bernd Edlinger -(Merged from https://github.com/openssl/openssl/pull/7486) - -(cherry picked from commit a9cfb8c2aa7254a4aa6a1716909e3f8cb78049b6) ---- - crypto/dsa/dsa_ossl.c | 28 +++++++++++++++------------- - 1 file changed, 15 insertions(+), 13 deletions(-) - -diff --git x/crypto/dsa/dsa_ossl.c y/crypto/dsa/dsa_ossl.c -index ca20811200..2dd2d7489a 100644 ---- x/crypto/dsa/dsa_ossl.c -+++ y/crypto/dsa/dsa_ossl.c -@@ -9,6 +9,7 @@ - - #include - #include "internal/cryptlib.h" -+#include "internal/bn_int.h" - #include - #include - #include "dsa_locl.h" -@@ -180,9 +181,9 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, - { - BN_CTX *ctx = NULL; - BIGNUM *k, *kinv = NULL, *r = *rp; -- BIGNUM *l, *m; -+ BIGNUM *l; - int ret = 0; -- int q_bits; -+ int q_bits, q_words; - - if (!dsa->p || !dsa->q || !dsa->g) { - DSAerr(DSA_F_DSA_SIGN_SETUP, DSA_R_MISSING_PARAMETERS); -@@ -191,8 +192,7 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, - - k = BN_new(); - l = BN_new(); -- m = BN_new(); -- if (k == NULL || l == NULL || m == NULL) -+ if (k == NULL || l == NULL) - goto err; - - if (ctx_in == NULL) { -@@ -203,9 +203,9 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, - - /* Preallocate space */ - q_bits = BN_num_bits(dsa->q); -- if (!BN_set_bit(k, q_bits) -- || !BN_set_bit(l, q_bits) -- || !BN_set_bit(m, q_bits)) -+ q_words = bn_get_top(dsa->q); -+ if (!bn_wexpand(k, q_words + 2) -+ || !bn_wexpand(l, q_words + 2)) - goto err; - - /* Get random k */ -@@ -240,14 +240,17 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, - * small timing information leakage. We then choose the sum that is - * one bit longer than the modulus. - * -- * TODO: revisit the BN_copy aiming for a memory access agnostic -- * conditional copy. -+ * There are some concerns about the efficacy of doing this. More -+ * specificly refer to the discussion starting with: -+ * https://github.com/openssl/openssl/pull/7486#discussion_r228323705 -+ * The fix is to rework BN so these gymnastics aren't required. - */ - if (!BN_add(l, k, dsa->q) -- || !BN_add(m, l, dsa->q) -- || !BN_copy(k, BN_num_bits(l) > q_bits ? l : m)) -+ || !BN_add(k, l, dsa->q)) - goto err; - -+ BN_consttime_swap(BN_is_bit_set(l, q_bits), k, l, q_words + 2); -+ - if ((dsa)->meth->bn_mod_exp != NULL) { - if (!dsa->meth->bn_mod_exp(dsa, r, dsa->g, k, dsa->p, ctx, - dsa->method_mont_p)) -@@ -260,7 +263,7 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, - if (!BN_mod(r, r, dsa->q, ctx)) - goto err; - -- /* Compute part of 's = inv(k) (m + xr) mod q' */ -+ /* Compute part of 's = inv(k) (m + xr) mod q' */ - if ((kinv = dsa_mod_inverse_fermat(k, dsa->q, ctx)) == NULL) - goto err; - -@@ -275,7 +278,6 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, - BN_CTX_free(ctx); - BN_clear_free(k); - BN_clear_free(l); -- BN_clear_free(m); - return ret; - } - --- -2.19.1 - diff --git a/patches/openssl-Timing-vulnerability-in-ECDSA-signature-generation-C.patch b/patches/openssl-Timing-vulnerability-in-ECDSA-signature-generation-C.patch deleted file mode 100644 index 807236d..0000000 --- a/patches/openssl-Timing-vulnerability-in-ECDSA-signature-generation-C.patch +++ /dev/null @@ -1,44 +0,0 @@ -From b1d6d55ece1c26fa2829e2b819b038d7b6d692b4 Mon Sep 17 00:00:00 2001 -From: Pauli -Date: Fri, 26 Oct 2018 10:54:58 +1000 -Subject: [PATCH 3/3] Timing vulnerability in ECDSA signature generation - (CVE-2018-0735) - -Preallocate an extra limb for some of the big numbers to avoid a reallocation -that can potentially provide a side channel. - -Reviewed-by: Bernd Edlinger -(Merged from https://github.com/openssl/openssl/pull/7486) - -(cherry picked from commit 99540ec79491f59ed8b46b4edf130e17dc907f52) ---- - crypto/ec/ec_mult.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git x/crypto/ec/ec_mult.c y/crypto/ec/ec_mult.c -index 7e1b3650e7..0e0a5e1394 100644 ---- x/crypto/ec/ec_mult.c -+++ y/crypto/ec/ec_mult.c -@@ -206,8 +206,8 @@ int ec_scalar_mul_ladder(const EC_GROUP *group, EC_POINT *r, - */ - cardinality_bits = BN_num_bits(cardinality); - group_top = bn_get_top(cardinality); -- if ((bn_wexpand(k, group_top + 1) == NULL) -- || (bn_wexpand(lambda, group_top + 1) == NULL)) { -+ if ((bn_wexpand(k, group_top + 2) == NULL) -+ || (bn_wexpand(lambda, group_top + 2) == NULL)) { - ECerr(EC_F_EC_SCALAR_MUL_LADDER, ERR_R_BN_LIB); - goto err; - } -@@ -244,7 +244,7 @@ int ec_scalar_mul_ladder(const EC_GROUP *group, EC_POINT *r, - * k := scalar + 2*cardinality - */ - kbit = BN_is_bit_set(lambda, cardinality_bits); -- BN_consttime_swap(kbit, k, lambda, group_top + 1); -+ BN_consttime_swap(kbit, k, lambda, group_top + 2); - - group_top = bn_get_top(group->field); - if ((bn_wexpand(s->X, group_top) == NULL) --- -2.19.1 - diff --git a/patches/openssl-android-shlib_variant.patch b/patches/openssl-android-shlib_variant.patch deleted file mode 100644 index 2c8c7db..0000000 --- a/patches/openssl-android-shlib_variant.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- x/Configurations/15-android.conf -+++ y/Configurations/15-android.conf -@@ -136,6 +136,8 @@ - bn_ops => sub { android_ndk()->{bn_ops} }, - bin_cflags => "-pie", - enable => [ ], -+ shlib_variant => '-gov', -+ shared_extension => '.so', - }, - "android-arm" => { - ################################################################ diff --git a/patches/qt-Add-IsoDep-to-the-techList-on-Android.patch b/patches/qt-Add-IsoDep-to-the-techList-on-Android.patch deleted file mode 100644 index 4446aa2..0000000 --- a/patches/qt-Add-IsoDep-to-the-techList-on-Android.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 1d9b03157657e398a99f3f183bb7399b8905eb55 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Wed, 22 Nov 2017 07:35:56 +0100 -Subject: Add IsoDep to the techList on Android - -Change-Id: I26c183c1344cd0d9323fcedde82347487eebdffb ---- - src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java | 1 + - 1 file changed, 1 insertion(+) - -diff --git x/qtconnectivity/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java y/qtconnectivity/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java -index 345b87d3..a1ae5c37 100644 ---- x/qtconnectivity/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java -+++ y/qtconnectivity/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java -@@ -127,6 +127,7 @@ public class QtNfc - filters[2] = new IntentFilter(); - filters[2].addAction(NfcAdapter.ACTION_TECH_DISCOVERED); - String[][] techList = new String[][]{ -+ {"android.nfc.tech.IsoDep"}, - {"android.nfc.tech.Ndef"}, - {"android.nfc.tech.NdefFormatable"} - }; --- -2.18.0 - diff --git a/patches/qt-Android-Fix-crash.patch b/patches/qt-Android-Fix-crash.patch deleted file mode 100644 index e9b4415..0000000 --- a/patches/qt-Android-Fix-crash.patch +++ /dev/null @@ -1,30 +0,0 @@ -From ca8779363fd30a1b8fd80ce4ebacc4741b041c76 Mon Sep 17 00:00:00 2001 -From: BogDan Vatra -Date: Tue, 20 Mar 2018 10:36:43 +0200 -Subject: [PATCH] Android: Fix crash - -Android doesn't like nor use RTLD_NODELETE - -Tasnk-number: QTBUG-64654 -Change-Id: I2d884bbf22a681cca592942eba84ba97327ba974 -Reviewed-by: Eskil Abrahamsen Blomfeldt ---- - src/corelib/plugin/qlibrary_unix.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git x/qtbase/src/corelib/plugin/qlibrary_unix.cpp y/qtbase/src/corelib/plugin/qlibrary_unix.cpp -index 23b9ad6434..296af9da7a 100644 ---- x/qtbase/src/corelib/plugin/qlibrary_unix.cpp -+++ y/qtbase/src/corelib/plugin/qlibrary_unix.cpp -@@ -155,7 +155,7 @@ bool QLibraryPrivate::load_sys() - // Do not unload the library during dlclose(). Consequently, the - // library's specific static variables are not reinitialized if the - // library is reloaded with dlopen() at a later time. --#ifdef RTLD_NODELETE -+#if defined(RTLD_NODELETE) && !defined(Q_OS_ANDROID) - if (loadHints & QLibrary::PreventUnloadHint) { - dlFlags |= RTLD_NODELETE; - } --- -2.19.1 - diff --git a/patches/qt-Android-fix-compile-with-NDK-r18.patch b/patches/qt-Android-fix-compile-with-NDK-r18.patch deleted file mode 100644 index 4fcd09e..0000000 --- a/patches/qt-Android-fix-compile-with-NDK-r18.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 48789f354ffe99aa28c08f55240e0b0a4deaa377 Mon Sep 17 00:00:00 2001 -From: BogDan Vatra -Date: Mon, 1 Oct 2018 15:46:46 +0300 -Subject: [PATCH] Android: fix compile with NDK r18+ - -In NDK r18, libc++.so was renamed to libc++.so.XX where XX is the Android -API level. - -[ChangeLog][Android] Fixed build issue with NDK r18+. - -Task-number: QTBUG-70631 -Task-number: QTBUG-70779 -Change-Id: Id0d2955648197e3054e3c69263b5a90d57964f6c ---- - mkspecs/android-clang/qmake.conf | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git x/qtbase/mkspecs/android-clang/qmake.conf y/qtbase/mkspecs/android-clang/qmake.conf -index b665000d00..1f5e690329 100644 ---- x/qtbase/mkspecs/android-clang/qmake.conf -+++ y/qtbase/mkspecs/android-clang/qmake.conf -@@ -40,7 +40,11 @@ QMAKE_CFLAGS += -DANDROID_HAS_WSTRING --sysroot=$$NDK_ROOT/sysroot \ - ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/libs/$$ANDROID_TARGET_ARCH - - ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so --ANDROID_CXX_STL_LIBS = -lc++ -+ -+exists($$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so): \ -+ ANDROID_CXX_STL_LIBS = -lc++ -+else: \ -+ ANDROID_CXX_STL_LIBS = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so.$$replace(ANDROID_PLATFORM, "android-", "") - - QMAKE_CFLAGS_OPTIMIZE_SIZE = -Oz - --- -2.19.0 - diff --git a/patches/qt-Introduce-reportError-to-fix-QMetaObject-invokeMethod.patch b/patches/qt-Introduce-reportError-to-fix-QMetaObject-invokeMethod.patch deleted file mode 100644 index 9967f2d..0000000 --- a/patches/qt-Introduce-reportError-to-fix-QMetaObject-invokeMethod.patch +++ /dev/null @@ -1,262 +0,0 @@ -From 9f00179a95ef729fa7871b4d408c76bc50e4eb4e Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Wed, 20 Jun 2018 11:56:19 +0200 -Subject: Introduce reportError to fix "QMetaObject::invokeMethod: No such method" - -Task-number: QTBUG-67958 -Change-Id: Ia5a21cb19f0318844ac436adcc3f0fff9a3185b5 -Reviewed-by: Alex Blasche ---- - src/nfc/qnearfieldtagtype1.cpp | 8 ++---- - src/nfc/qnearfieldtarget.cpp | 13 ++++++++++ - src/nfc/qnearfieldtarget.h | 2 ++ - src/nfc/qnearfieldtarget_android.cpp | 36 +++++++-------------------- - src/nfc/qnearfieldtarget_emulator.cpp | 20 ++++----------- - src/nfc/qnearfieldtarget_neard_p.h | 8 ++---- - 6 files changed, 33 insertions(+), 54 deletions(-) - -diff --git x/qtconnectivity/src/nfc/qnearfieldtagtype1.cpp y/qtconnectivity/src/nfc/qnearfieldtagtype1.cpp -index 34f2c8b8..7f27fbe9 100644 ---- x/qtconnectivity/src/nfc/qnearfieldtagtype1.cpp -+++ y/qtconnectivity/src/nfc/qnearfieldtagtype1.cpp -@@ -440,9 +440,7 @@ QNearFieldTarget::RequestId QNearFieldTagType1::readNdefMessages() - if (d->m_readNdefMessageState == QNearFieldTagType1Private::NotReadingNdefMessage) { - d->progressToNextNdefReadMessageState(); - } else { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, NdefReadError), -- Q_ARG(QNearFieldTarget::RequestId, d->m_readNdefRequestId)); -+ reportError(QNearFieldTarget::NdefReadError, d->m_readNdefRequestId); - } - - return d->m_readNdefRequestId; -@@ -462,9 +460,7 @@ QNearFieldTarget::RequestId QNearFieldTagType1::writeNdefMessages(const QListm_ndefWriteMessages = messages; - d->progressToNextNdefWriteMessageState(); - } else { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, NdefWriteError), -- Q_ARG(QNearFieldTarget::RequestId, d->m_readNdefRequestId)); -+ reportError(QNearFieldTarget::NdefWriteError, d->m_readNdefRequestId); - } - - return d->m_writeNdefRequestId; -diff --git x/qtconnectivity/src/nfc/qnearfieldtarget.cpp y/qtconnectivity/src/nfc/qnearfieldtarget.cpp -index e642824b..e9a6fa11 100644 ---- x/qtconnectivity/src/nfc/qnearfieldtarget.cpp -+++ y/qtconnectivity/src/nfc/qnearfieldtarget.cpp -@@ -530,4 +530,17 @@ bool QNearFieldTarget::handleResponse(const QNearFieldTarget::RequestId &id, - return true; - } - -+/*! -+ \since 5.12 -+ -+ Reports the \a error for the request \a id by appending the signal emission to the event queue. -+*/ -+void QNearFieldTarget::reportError(QNearFieldTarget::Error error, -+ const QNearFieldTarget::RequestId &id) -+{ -+ QMetaObject::invokeMethod(this, [this, error, id]() { -+ Q_EMIT this->error(error, id); -+ }, Qt::QueuedConnection); -+} -+ - QT_END_NAMESPACE -diff --git x/qtconnectivity/src/nfc/qnearfieldtarget.h y/qtconnectivity/src/nfc/qnearfieldtarget.h -index e51960f7..868b52d5 100644 ---- x/qtconnectivity/src/nfc/qnearfieldtarget.h -+++ y/qtconnectivity/src/nfc/qnearfieldtarget.h -@@ -153,6 +153,8 @@ protected: - Q_INVOKABLE virtual bool handleResponse(const QNearFieldTarget::RequestId &id, - const QByteArray &response); - -+ void reportError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id); -+ - Q_SIGNALS: - void disconnected(); - -diff --git x/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp y/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp -index e656996e..78da6ac2 100644 ---- x/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp -+++ y/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp -@@ -147,25 +147,19 @@ QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages() - // Making sure that target is still in range - QNearFieldTarget::RequestId requestId(new QNearFieldTarget::RequestIdPrivate); - if (!m_intent.isValid()) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), -- Q_ARG(QNearFieldTarget::RequestId&, requestId)); -+ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId); - return requestId; - } - - // Getting Ndef technology object - if (!setTagTechnology({NDEFTECHNOLOGY})) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnsupportedError), -- Q_ARG(QNearFieldTarget::RequestId&, requestId)); -+ reportError(QNearFieldTarget::UnsupportedError, requestId); - return requestId; - } - - // Connect - if (!connect()) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), -- Q_ARG(QNearFieldTarget::RequestId&, requestId)); -+ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId); - return requestId; - } - -@@ -174,9 +168,7 @@ QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages() - if (catchJavaExceptions()) - ndefMessage = QAndroidJniObject(); - if (!ndefMessage.isValid()) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::NdefReadError), -- Q_ARG(QNearFieldTarget::RequestId&, requestId)); -+ reportError(QNearFieldTarget::NdefReadError, requestId); - return requestId; - } - -@@ -249,9 +241,7 @@ QNearFieldTarget::RequestId NearFieldTarget::sendCommand(const QByteArray &comma - // Connecting - QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate()); - if (!connect()) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), -- Q_ARG(QNearFieldTarget::RequestId&, requestId)); -+ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId); - return requestId; - } - -@@ -263,9 +253,7 @@ QNearFieldTarget::RequestId NearFieldTarget::sendCommand(const QByteArray &comma - // Writing - QAndroidJniObject myNewVal = m_tagTech.callObjectMethod("transceive", "([B)[B", jba); - if (catchJavaExceptions()) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::CommandError), -- Q_ARG(QNearFieldTarget::RequestId&, requestId)); -+ reportError(QNearFieldTarget::CommandError, requestId); - return requestId; - } - QByteArray result = jbyteArrayToQByteArray(myNewVal.object()); -@@ -315,9 +303,7 @@ QNearFieldTarget::RequestId NearFieldTarget::writeNdefMessages(const QListSetByteArrayRegion(jba.object(), 0, ba.size(), reinterpret_cast(ba.data())); - QAndroidJniObject jmessage = QAndroidJniObject("android/nfc/NdefMessage", "([B)V", jba.object()); - if (catchJavaExceptions()) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnknownError), -- Q_ARG(QNearFieldTarget::RequestId&, requestId)); -+ reportError(QNearFieldTarget::UnknownError, requestId); - return requestId; - } - - // Writing - m_tagTech.callMethod(writeMethod, "(Landroid/nfc/NdefMessage;)V", jmessage.object()); - if (catchJavaExceptions()) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::NdefWriteError), -- Q_ARG(QNearFieldTarget::RequestId&, requestId)); -+ reportError(QNearFieldTarget::NdefWriteError, requestId); - return requestId; - } - -diff --git x/qtconnectivity/src/nfc/qnearfieldtarget_emulator.cpp y/qtconnectivity/src/nfc/qnearfieldtarget_emulator.cpp -index 29b1f74d..030718cc 100644 ---- x/qtconnectivity/src/nfc/qnearfieldtarget_emulator.cpp -+++ y/qtconnectivity/src/nfc/qnearfieldtarget_emulator.cpp -@@ -82,9 +82,7 @@ QNearFieldTarget::RequestId TagType1::sendCommand(const QByteArray &command) - - // tag not in proximity - if (!tagMap.value(m_tag)) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, TargetOutOfRangeError), -- Q_ARG(QNearFieldTarget::RequestId, id)); -+ reportError(QNearFieldTarget::TargetOutOfRangeError, id); - return id; - } - -@@ -93,17 +91,13 @@ QNearFieldTarget::RequestId TagType1::sendCommand(const QByteArray &command) - QByteArray response = m_tag->processCommand(command + char(crc & 0xff) + char(crc >> 8)); - - if (response.isEmpty()) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, NoResponseError), -- Q_ARG(QNearFieldTarget::RequestId, id)); -+ reportError(QNearFieldTarget::NoResponseError, id); - return id; - } - - // check crc - if (qChecksum(response.constData(), response.length(), Qt::ChecksumItuV41) != 0) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, ChecksumMismatchError), -- Q_ARG(QNearFieldTarget::RequestId, id)); -+ reportError(QNearFieldTarget::ChecksumMismatchError, id); - return id; - } - -@@ -152,9 +146,7 @@ QNearFieldTarget::RequestId TagType2::sendCommand(const QByteArray &command) - - // tag not in proximity - if (!tagMap.value(m_tag)) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, TargetOutOfRangeError), -- Q_ARG(QNearFieldTarget::RequestId, id)); -+ reportError(QNearFieldTarget::TargetOutOfRangeError, id); - return id; - } - -@@ -168,9 +160,7 @@ QNearFieldTarget::RequestId TagType2::sendCommand(const QByteArray &command) - if (response.length() > 1) { - // check crc - if (qChecksum(response.constData(), response.length(), Qt::ChecksumItuV41) != 0) { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, ChecksumMismatchError), -- Q_ARG(QNearFieldTarget::RequestId, id)); -+ reportError(QNearFieldTarget::ChecksumMismatchError, id); - return id; - } - -diff --git x/qtconnectivity/src/nfc/qnearfieldtarget_neard_p.h y/qtconnectivity/src/nfc/qnearfieldtarget_neard_p.h -index 053df141..625cee67 100644 ---- x/qtconnectivity/src/nfc/qnearfieldtarget_neard_p.h -+++ y/qtconnectivity/src/nfc/qnearfieldtarget_neard_p.h -@@ -359,9 +359,7 @@ private: - Q_EMIT this->requestCompleted(this->m_currentReadRequestId); - }, Qt::QueuedConnection); - } else { -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnknownError), -- Q_ARG(QNearFieldTarget::RequestId, m_currentReadRequestId)); -+ this->reportError(QNearFieldTarget::UnknownError, m_currentReadRequestId); - } - - m_readRequested = false; -@@ -389,9 +387,7 @@ private: - reply.waitForFinished(); - if (reply.isError()) { - qCWarning(QT_NFC_NEARD) << "Error writing to NFC tag" << reply.error(); -- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -- Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnknownError), -- Q_ARG(QNearFieldTarget::RequestId, m_currentWriteRequestId)); -+ this->reportError(QNearFieldTarget::UnknownError, m_currentWriteRequestId); - } - - QMetaObject::invokeMethod(this, "ndefMessagesWritten", Qt::QueuedConnection); --- -2.19.1 - diff --git a/patches/qt-Linux-Remove-our-use-of-syscall-for-statx-2-and-rena.patch b/patches/qt-Linux-Remove-our-use-of-syscall-for-statx-2-and-rena.patch deleted file mode 100644 index 3b4a6cf..0000000 --- a/patches/qt-Linux-Remove-our-use-of-syscall-for-statx-2-and-rena.patch +++ /dev/null @@ -1,135 +0,0 @@ -From f65d3fbf6cc8a50a2b6b1b5632b9b9cf189054f0 Mon Sep 17 00:00:00 2001 -From: Thiago Macieira -Date: Wed, 19 Sep 2018 00:05:54 -0500 -Subject: [PATCH] Linux: Remove our use of syscall() for statx(2) and - renameat2(2) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Those system calls are present in glibc 2.28. Instead of using -syscall(3) to place the system calls directly, let's use only the glibc -functions. That also means we no longer accept ENOSYS from either -function, if they were detected in glibc. - -Change-Id: I44e7d800c68141bdaae0fffd1555b4b8fe63786b -Reviewed-by: Oswald Buddenhagen -Reviewed-by: Lars Knoll -Reviewed-by: Jüri Valdmann ---- - src/corelib/global/minimum-linux_p.h | 7 +++- - src/corelib/io/qfilesystemengine_unix.cpp | 45 ++--------------------- - 2 files changed, 9 insertions(+), 43 deletions(-) - -diff --git x/qtbase/src/corelib/global/minimum-linux_p.h y/qtbase/src/corelib/global/minimum-linux_p.h -index bad2488b4d..9c074e13ba 100644 ---- x/qtbase/src/corelib/global/minimum-linux_p.h -+++ y/qtbase/src/corelib/global/minimum-linux_p.h -@@ -75,9 +75,14 @@ QT_BEGIN_NAMESPACE - * - accept4 2.6.28 - * - renameat2 3.16 QT_CONFIG(renameat2) - * - getrandom 3.17 QT_CONFIG(getentropy) -+ * - statx 4.11 QT_CONFIG(statx) - */ - --#if QT_CONFIG(getentropy) -+#if QT_CONFIG(statx) -+# define MINLINUX_MAJOR 4 -+# define MINLINUX_MINOR 11 -+# define MINLINUX_PATCH 0 -+#elif QT_CONFIG(getentropy) - # define MINLINUX_MAJOR 3 - # define MINLINUX_MINOR 17 - # define MINLINUX_PATCH 0 -diff --git x/qtbase/src/corelib/io/qfilesystemengine_unix.cpp y/qtbase/src/corelib/io/qfilesystemengine_unix.cpp -index deb4a9f220..40e8f82a80 100644 ---- x/qtbase/src/corelib/io/qfilesystemengine_unix.cpp -+++ y/qtbase/src/corelib/io/qfilesystemengine_unix.cpp -@@ -1,6 +1,6 @@ - /**************************************************************************** - ** --** Copyright (C) 2017 Intel Corporation. -+** Copyright (C) 2018 Intel Corporation. - ** Copyright (C) 2016 The Qt Company Ltd. - ** Copyright (C) 2013 Samuel Gaist - ** Contact: https://www.qt.io/licensing/ -@@ -88,7 +88,6 @@ extern "C" NSString *NSTemporaryDirectory(); - - #if defined(Q_OS_LINUX) - # include --# include - # include - # include - -@@ -96,28 +95,6 @@ extern "C" NSString *NSTemporaryDirectory(); - #ifndef FICLONE - # define FICLONE _IOW(0x94, 9, int) - #endif -- --# if defined(Q_OS_ANDROID) --// renameat2() and statx() are disabled on Android because quite a few systems --// come with sandboxes that kill applications that make system calls outside a --// whitelist and several Android vendors can't be bothered to update the list. --# undef SYS_renameat2 --# undef SYS_statx --# undef STATX_BASIC_STATS --# else --# if !QT_CONFIG(renameat2) && defined(SYS_renameat2) --static int renameat2(int oldfd, const char *oldpath, int newfd, const char *newpath, unsigned flags) --{ return syscall(SYS_renameat2, oldfd, oldpath, newfd, newpath, flags); } --# endif -- --# if !QT_CONFIG(statx) && defined(SYS_statx) --# include --static int statx(int dirfd, const char *pathname, int flag, unsigned mask, struct statx *statxbuf) --{ return syscall(SYS_statx, dirfd, pathname, flag, mask, statxbuf); } --# elif !QT_CONFIG(statx) && !defined(SYS_statx) --# undef STATX_BASIC_STATS --# endif --# endif // !Q_OS_ANDROID - #endif - - #ifndef STATX_ALL -@@ -331,22 +308,8 @@ mtime(const T &statBuffer, int) - #ifdef STATX_BASIC_STATS - static int qt_real_statx(int fd, const char *pathname, int flags, struct statx *statxBuffer) - { --#ifdef Q_ATOMIC_INT8_IS_SUPPORTED -- static QBasicAtomicInteger statxTested = Q_BASIC_ATOMIC_INITIALIZER(0); --#else -- static QBasicAtomicInt statxTested = Q_BASIC_ATOMIC_INITIALIZER(0); --#endif -- -- if (statxTested.load() == -1) -- return -ENOSYS; -- - unsigned mask = STATX_BASIC_STATS | STATX_BTIME; - int ret = statx(fd, pathname, flags, mask, statxBuffer); -- if (ret == -1 && errno == ENOSYS) { -- statxTested.store(-1); -- return -ENOSYS; -- } -- statxTested.store(1); - return ret == -1 ? -errno : 0; - } - -@@ -1282,14 +1245,12 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy - if (Q_UNLIKELY(srcPath.isEmpty() || tgtPath.isEmpty())) - return emptyFileEntryWarning(), false; - --#if defined(RENAME_NOREPLACE) && (QT_CONFIG(renameat2) || defined(SYS_renameat2)) -+#if defined(RENAME_NOREPLACE) && QT_CONFIG(renameat2) - if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0) - return true; - -- // If we're using syscall(), check for ENOSYS; -- // if renameat2 came from libc, we don't accept ENOSYS. - // We can also get EINVAL for some non-local filesystems. -- if ((QT_CONFIG(renameat2) || errno != ENOSYS) && errno != EINVAL) { -+ if (errno != EINVAL) { - error = QSystemError(errno, QSystemError::StandardLibraryError); - return false; - } --- -2.20.1 - diff --git a/patches/qt-QObject-Fix-isSignalConnected-when-signals-have-been.patch b/patches/qt-QObject-Fix-isSignalConnected-when-signals-have-been.patch deleted file mode 100644 index f80031e..0000000 --- a/patches/qt-QObject-Fix-isSignalConnected-when-signals-have-been.patch +++ /dev/null @@ -1,185 +0,0 @@ -From 78ab3263caae535a3bd31fa35c733ae2a28ca8ba Mon Sep 17 00:00:00 2001 -From: Kari Oikarinen -Date: Wed, 26 Sep 2018 10:29:14 +0300 -Subject: [PATCH] QObject: Fix isSignalConnected() when signals have been - disconnected -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The bitmap cache for the first 64 signals being connected was only set when the -connection is added. It was never unset when the connection was removed. - -Internal use of the connectedSignals bitmap is not hurt by it occasionally -saying a signal is connected even though it is not, since the purpose of those -checks is avoiding expensive operations that are not necessary if nothing is -connected to the signal. - -However, the public API using this cache meant that it also never spotted -signals being disconnected. This was not documented. Fix the behavior by only -using the cache if it is up to date. If it is not, use a slower path that gives -the correct answer. - -To avoid making disconnections and QObject destructions slower, the cache is -only updated to unset disconnected signals when new signal connections are -added. No extra work is done in the common case where signals are only -removed in the end of the QObject's lifetime. - -Fixes: QTBUG-32340 -Change-Id: Ieb6e498060157153cec60d9c8f1c33056993fda1 -Reviewed-by: Ville Voutilainen -Reviewed-by: Thiago Macieira -Reviewed-by: Olivier Goffart (Woboq GmbH) -Reviewed-by: Jędrzej Nowacki ---- - src/corelib/kernel/qobject.cpp | 34 ++++++++---- - .../corelib/kernel/qobject/tst_qobject.cpp | 53 +++++++++++++++++++ - 2 files changed, 76 insertions(+), 11 deletions(-) - -diff --git x/qtbase/src/corelib/kernel/qobject.cpp y/qtbase/src/corelib/kernel/qobject.cpp -index c6fe787e03..4532eacf0c 100644 ---- x/qtbase/src/corelib/kernel/qobject.cpp -+++ y/qtbase/src/corelib/kernel/qobject.cpp -@@ -418,6 +418,7 @@ void QObjectPrivate::cleanConnectionLists() - { - if (connectionLists->dirty && !connectionLists->inUse) { - // remove broken connections -+ bool allConnected = false; - for (int signal = -1; signal < connectionLists->count(); ++signal) { - QObjectPrivate::ConnectionList &connectionList = - (*connectionLists)[signal]; -@@ -429,11 +430,13 @@ void QObjectPrivate::cleanConnectionLists() - - QObjectPrivate::Connection **prev = &connectionList.first; - QObjectPrivate::Connection *c = *prev; -+ bool connected = false; // whether the signal is still connected somewhere - while (c) { - if (c->receiver) { - last = c; - prev = &c->nextConnectionList; - c = *prev; -+ connected = true; - } else { - QObjectPrivate::Connection *next = c->nextConnectionList; - *prev = next; -@@ -445,6 +448,14 @@ void QObjectPrivate::cleanConnectionLists() - // Correct the connection list's last pointer. - // As conectionList.last could equal last, this could be a noop - connectionList.last = last; -+ -+ if (!allConnected && !connected && signal >= 0 -+ && size_t(signal) < sizeof(connectedSignals) * 8) { -+ // This signal is no longer connected -+ connectedSignals[signal >> 5] &= ~(1 << (signal & 0x1f)); -+ } else if (signal == -1) { -+ allConnected = connected; -+ } - } - connectionLists->dirty = false; - } -@@ -2503,19 +2514,20 @@ bool QObject::isSignalConnected(const QMetaMethod &signal) const - - signalIndex += QMetaObjectPrivate::signalOffset(signal.mobj); - -- if (signalIndex < sizeof(d->connectedSignals) * 8) -+ QMutexLocker locker(signalSlotLock(this)); -+ if (!d->connectionLists) -+ return false; -+ -+ if (signalIndex < sizeof(d->connectedSignals) * 8 && !d->connectionLists->dirty) - return d->isSignalConnected(signalIndex); - -- QMutexLocker locker(signalSlotLock(this)); -- if (d->connectionLists) { -- if (signalIndex < uint(d->connectionLists->count())) { -- const QObjectPrivate::Connection *c = -- d->connectionLists->at(signalIndex).first; -- while (c) { -- if (c->receiver) -- return true; -- c = c->nextConnectionList; -- } -+ if (signalIndex < uint(d->connectionLists->count())) { -+ const QObjectPrivate::Connection *c = -+ d->connectionLists->at(signalIndex).first; -+ while (c) { -+ if (c->receiver) -+ return true; -+ c = c->nextConnectionList; - } - } - return false; -diff --git x/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp y/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp -index ec57522f48..20ce905265 100644 ---- x/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp -+++ y/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp -@@ -104,6 +104,7 @@ private slots: - void deleteQObjectWhenDeletingEvent(); - void overloads(); - void isSignalConnected(); -+ void isSignalConnectedAfterDisconnection(); - void qMetaObjectConnect(); - void qMetaObjectDisconnectOne(); - void sameName(); -@@ -3843,6 +3844,58 @@ void tst_QObject::isSignalConnected() - QVERIFY(!o.isSignalConnected(QMetaMethod())); - } - -+void tst_QObject::isSignalConnectedAfterDisconnection() -+{ -+ ManySignals o; -+ const QMetaObject *meta = o.metaObject(); -+ -+ const QMetaMethod sig00 = meta->method(meta->indexOfSignal("sig00()")); -+ QVERIFY(!o.isSignalConnected(sig00)); -+ QObject::connect(&o, &ManySignals::sig00, qt_noop); -+ QVERIFY(o.isSignalConnected(sig00)); -+ QVERIFY(QObject::disconnect(&o, &ManySignals::sig00, 0, 0)); -+ QVERIFY(!o.isSignalConnected(sig00)); -+ -+ const QMetaMethod sig69 = meta->method(meta->indexOfSignal("sig69()")); -+ QVERIFY(!o.isSignalConnected(sig69)); -+ QObject::connect(&o, &ManySignals::sig69, qt_noop); -+ QVERIFY(o.isSignalConnected(sig69)); -+ QVERIFY(QObject::disconnect(&o, &ManySignals::sig69, 0, 0)); -+ QVERIFY(!o.isSignalConnected(sig69)); -+ -+ { -+ ManySignals o2; -+ QObject::connect(&o, &ManySignals::sig00, &o2, &ManySignals::sig00); -+ QVERIFY(o.isSignalConnected(sig00)); -+ // o2 is destructed -+ } -+ QVERIFY(!o.isSignalConnected(sig00)); -+ -+ const QMetaMethod sig01 = meta->method(meta->indexOfSignal("sig01()")); -+ QObject::connect(&o, &ManySignals::sig00, qt_noop); -+ QObject::connect(&o, &ManySignals::sig01, qt_noop); -+ QObject::connect(&o, &ManySignals::sig69, qt_noop); -+ QVERIFY(o.isSignalConnected(sig00)); -+ QVERIFY(o.isSignalConnected(sig01)); -+ QVERIFY(o.isSignalConnected(sig69)); -+ QVERIFY(QObject::disconnect(&o, &ManySignals::sig69, 0, 0)); -+ QVERIFY(o.isSignalConnected(sig00)); -+ QVERIFY(o.isSignalConnected(sig01)); -+ QVERIFY(!o.isSignalConnected(sig69)); -+ QVERIFY(QObject::disconnect(&o, &ManySignals::sig00, 0, 0)); -+ QVERIFY(!o.isSignalConnected(sig00)); -+ QVERIFY(o.isSignalConnected(sig01)); -+ QVERIFY(!o.isSignalConnected(sig69)); -+ QObject::connect(&o, &ManySignals::sig69, qt_noop); -+ QVERIFY(!o.isSignalConnected(sig00)); -+ QVERIFY(o.isSignalConnected(sig01)); -+ QVERIFY(o.isSignalConnected(sig69)); -+ QVERIFY(QObject::disconnect(&o, &ManySignals::sig01, 0, 0)); -+ QVERIFY(!o.isSignalConnected(sig00)); -+ QVERIFY(!o.isSignalConnected(sig01)); -+ QVERIFY(o.isSignalConnected(sig69)); -+} -+ - void tst_QObject::qMetaObjectConnect() - { - SenderObject *s = new SenderObject; --- -2.19.1 - diff --git a/patches/qt-QUrl-Support-IPv6-addresses-with-zone-id.patch b/patches/qt-QUrl-Support-IPv6-addresses-with-zone-id.patch deleted file mode 100644 index bd5616f..0000000 --- a/patches/qt-QUrl-Support-IPv6-addresses-with-zone-id.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 2e492dc6a6cf9e73a04f65e133ea4e97324a68da Mon Sep 17 00:00:00 2001 -From: Robbert Proost -Date: Thu, 18 Jan 2018 09:52:49 +0100 -Subject: [PATCH] QUrl: Support IPv6 addresses with zone id - -Task-number: QTBUG-25550 -Change-Id: I37ec02b655abe2779aa11945e20550ce00e43723 ---- - src/corelib/io/qurl.cpp | 63 ++++++++++++++++--------- - tests/auto/corelib/io/qurl/tst_qurl.cpp | 52 ++++++++++++++++++++ - 2 files changed, 92 insertions(+), 23 deletions(-) - -diff --git x/qtbase/src/corelib/io/qurl.cpp y/qtbase/src/corelib/io/qurl.cpp -index 4587b9fcd6..e2a66c8459 100644 ---- x/qtbase/src/corelib/io/qurl.cpp -+++ y/qtbase/src/corelib/io/qurl.cpp -@@ -1203,16 +1203,18 @@ inline void QUrlPrivate::setQuery(const QString &value, int from, int iend) - - inline void QUrlPrivate::appendHost(QString &appendTo, QUrl::FormattingOptions options) const - { -- // EncodeUnicode is the only flag that matters -- if ((options & QUrl::FullyDecoded) == QUrl::FullyDecoded) -- options = 0; -- else -- options &= QUrl::EncodeUnicode; - if (host.isEmpty()) - return; - if (host.at(0).unicode() == '[') { -- // IPv6Address and IPvFuture address never require any transformation -- appendTo += host; -+ // IPv6 addresses might contain a zone-id which needs to be recoded -+ QString hostInCorrectFormat; -+ if (options != 0) -+ qt_urlRecode(hostInCorrectFormat, host.constBegin(), host.constEnd(), options, 0); -+ -+ if (hostInCorrectFormat.isEmpty()) -+ hostInCorrectFormat = host; -+ -+ appendTo += hostInCorrectFormat; - } else { - // this is either an IPv4Address or a reg-name - // if it is a reg-name, it is already stored in Unicode form -@@ -1278,31 +1280,46 @@ static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar - // ONLY the IPv6 address is parsed here, WITHOUT the brackets - static const QChar *parseIp6(QString &host, const QChar *begin, const QChar *end, QUrl::ParsingMode mode) - { -- QIPAddressUtils::IPv6Address address; -- const QChar *ret = QIPAddressUtils::parseIp6(address, begin, end); -- if (ret) { -- // this struct is kept in automatic storage because it's only 4 bytes -+ QString decoded; -+ if (mode == QUrl::TolerantMode) { - const ushort decodeColon[] = { decode(':'), 0 }; -+ if (qt_urlRecode(decoded, begin, end, QUrl::ComponentFormattingOption::PrettyDecoded, decodeColon) == 0) { -+ decoded = QString(begin, end-begin); -+ } -+ } -+ else { -+ decoded = QString(begin, end-begin); -+ } - -- // IPv6 failed parsing, check if it was a percent-encoded character in -- // the middle and try again -- QString decoded; -- if (mode == QUrl::TolerantMode && qt_urlRecode(decoded, begin, end, 0, decodeColon)) { -- // recurse -- // if the parsing fails again, the qt_urlRecode above will return 0 -- ret = parseIp6(host, decoded.constBegin(), decoded.constEnd(), mode); -+ const QLatin1String zoneIdIdentifier("%25"); -+ QIPAddressUtils::IPv6Address address; -+ QString zoneId; -+ -+ const QChar *endBeforeZoneId = decoded.constEnd(); -+ -+ int zoneIdPosition = decoded.indexOf(zoneIdIdentifier); -+ if ((zoneIdPosition != -1) && (decoded.lastIndexOf(zoneIdIdentifier) == zoneIdPosition)) { -+ zoneId = decoded.mid(zoneIdPosition + zoneIdIdentifier.size()); -+ endBeforeZoneId = decoded.constBegin() + zoneIdPosition; - -- // we can't return ret, otherwise it would be dangling -- return ret ? end : 0; -+ if (zoneId.isEmpty() == true) { -+ return end; - } -+ } - -- // no transformation, nothing to re-parse -- return ret; -+ const QChar *ret = QIPAddressUtils::parseIp6(address, decoded.constBegin(), endBeforeZoneId); -+ if (ret) { -+ return begin + (ret - decoded.constBegin()); - } - -- host.reserve(host.size() + (end - begin)); -+ host.reserve(host.size() + (decoded.constEnd() - decoded.constBegin())); - host += QLatin1Char('['); - QIPAddressUtils::toString(host, address); -+ -+ if (zoneId.isEmpty() == false) { -+ host += zoneIdIdentifier; -+ host += zoneId; -+ } - host += QLatin1Char(']'); - return 0; - } --- -2.18.0 - diff --git a/patches/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch b/patches/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch deleted file mode 100644 index 0c7dea9..0000000 --- a/patches/qt-Remove-Qt-Labs-specific-plugins-from-the-build.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 2e80dec588c21cfeb086912cf6c6a011c6b6b896 Mon Sep 17 00:00:00 2001 -From f3e9a6e63740d922577d331f6cbe57fd43888472 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Mon, 16 Apr 2018 08:34:42 +0200 -Subject: Remove Qt Labs specific plugins from the build ---- - qtquickcontrols2/src/imports/imports.pro | 2 -- - qtdeclarative/src/imports/imports.pro | 4 ---- - 2 file changed, 6 deletions(-) - -diff --git x/qtquickcontrols2/src/imports/imports.pro y/qtquickcontrols2/src/imports/imports.pro -index e32bded7..944c9292 100644 ---- x/qtquickcontrols2/src/imports/imports.pro -+++ y/qtquickcontrols2/src/imports/imports.pro -@@ -1,8 +1,6 @@ - TEMPLATE = subdirs - SUBDIRS += \ - controls \ -- calendar \ -- platform \ - templates - - SUBDIRS += \ -diff --git x/qtdeclarative/src/imports/imports.pro y/qtdeclarative/src/imports/imports.pro -index 5d7e43488..33390bc42 100644 ---- x/qtdeclarative/src/imports/imports.pro -+++ y/qtdeclarative/src/imports/imports.pro -@@ -5,7 +5,6 @@ - SUBDIRS += \ - builtins \ - qtqml \ -- folderlistmodel \ - models - - qtHaveModule(sql): SUBDIRS += localstorage -@@ -14,13 +13,11 @@ - - qtHaveModule(quick) { - SUBDIRS += \ -- handlers \ - layouts \ - qtquick2 \ - window - - qtHaveModule(testlib): SUBDIRS += testlib -- qtConfig(systemsemaphore): SUBDIRS += sharedimage - qtConfig(quick-particles): \ - SUBDIRS += particles - --- -2.16.2 - diff --git a/patches/qt-Use-QUrl-toString-when-forming-the-Host-header.patch b/patches/qt-Use-QUrl-toString-when-forming-the-Host-header.patch deleted file mode 100644 index f9b4ab3..0000000 --- a/patches/qt-Use-QUrl-toString-when-forming-the-Host-header.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 4ab766863d88a491f91fa81731dbde75d0122d89 Mon Sep 17 00:00:00 2001 -From: Timur Pocheptsov -Date: Mon, 14 May 2018 12:41:23 +0200 -Subject: [PATCH] Use QUrl::toString() when forming the 'Host' header -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This allows to correctly use IPv6 literals and also -deals (correctly) with a port (if it's set at all). - -Task-number: QTBUG-68245 -Change-Id: I6d29543887c4ab58d70f0970a6f0a1b822c301df -Reviewed-by: Thiago Macieira -Reviewed-by: Mårten Nordheim ---- - src/websockets/qwebsocket_p.cpp | 22 ++++++++++++---------- - 1 file changed, 12 insertions(+), 10 deletions(-) - -diff --git x/qtwebsockets/src/websockets/qwebsocket_p.cpp y/qtwebsockets/src/websockets/qwebsocket_p.cpp -index d233b66..9b27ad2 100644 ---- x/qtwebsockets/src/websockets/qwebsocket_p.cpp -+++ y/qtwebsockets/src/websockets/qwebsocket_p.cpp -@@ -1108,16 +1108,18 @@ void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketS - headers << qMakePair(QString::fromLatin1(key), - QString::fromLatin1(m_request.rawHeader(key))); - -- const QString handshake = -- createHandShakeRequest(m_resourceName, -- m_request.url().host() -- % QStringLiteral(":") -- % QString::number(m_request.url().port(port)), -- origin(), -- QString(), -- QString(), -- m_key, -- headers); -+ const auto format = QUrl::RemoveScheme | QUrl::RemoveUserInfo -+ | QUrl::RemovePath | QUrl::RemoveQuery -+ | QUrl::RemoveFragment | QUrl::RemovePort; -+ const QString host = m_request.url().toString(format).mid(2); -+ const QString handshake = createHandShakeRequest(m_resourceName, -+ host % QStringLiteral(":") -+ % QString::number(m_request.url().port(port)), -+ origin(), -+ QString(), -+ QString(), -+ m_key, -+ headers); - if (handshake.isEmpty()) { - m_pSocket->abort(); - Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); --- -2.19.0 - diff --git a/patches/qt-Use-user-provided-session-data-if-available.patch b/patches/qt-Use-user-provided-session-data-if-available.patch deleted file mode 100644 index 30b05fb..0000000 --- a/patches/qt-Use-user-provided-session-data-if-available.patch +++ /dev/null @@ -1,33 +0,0 @@ -From bbd5c6feea8632c8321d3cd7a16a262d90f856e8 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= -Date: Thu, 22 Nov 2018 11:44:39 +0100 -Subject: [PATCH] Use user provided session data if available - -If a user uses persistent sessions and provide the session to -Qt again with setSessionTicket it should be used with a higher -priority. -This is also a work-around for QTBUG-71967. - -Change-Id: I7351b669b6de2863136d6106dc4f73fa5c7b8c51 ---- - src/network/ssl/qsslcontext_openssl.cpp | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git x/qtbase/src/network/ssl/qsslcontext_openssl.cpp y/qtbase/src/network/ssl/qsslcontext_openssl.cpp -index 41b759364b..24dbc9ecbc 100644 ---- x/qtbase/src/network/ssl/qsslcontext_openssl.cpp -+++ y/qtbase/src/network/ssl/qsslcontext_openssl.cpp -@@ -134,8 +134,9 @@ SSL* QSslContext::createSsl() - SSL* ssl = q_SSL_new(ctx); - q_SSL_clear(ssl); - -- if (!session && !sessionASN1().isEmpty() -+ if (!m_sessionASN1.isEmpty() - && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) { -+ q_SSL_SESSION_free(session); - const unsigned char *data = reinterpret_cast(m_sessionASN1.constData()); - session = q_d2i_SSL_SESSION(0, &data, m_sessionASN1.size()); // refcount is 1 already, set by function above - } --- -2.19.1 - diff --git a/patches/qt-Work-Around-FreeBSD-v12-build.patch b/patches/qt-Work-Around-FreeBSD-v12-build.patch deleted file mode 100644 index 46be5b2..0000000 --- a/patches/qt-Work-Around-FreeBSD-v12-build.patch +++ /dev/null @@ -1,30 +0,0 @@ -From ec00fb42be3206956fd9ac7518018b47652f2bb8 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= -Date: Fri, 28 Dec 2018 16:12:16 +0100 -Subject: [PATCH] Work-Around FreeBSD v12 build - -https://bugreports.qt.io/browse/QTBUG-72775 - -Change-Id: Ib39e8e488c2abd0321e91acd15614085c7e2156b ---- - src/network/kernel/qnetworkinterface_unix.cpp | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git x/qtbase/src/network/kernel/qnetworkinterface_unix.cpp y/qtbase/src/network/kernel/qnetworkinterface_unix.cpp -index d69fc47667..eebca77189 100644 ---- x/qtbase/src/network/kernel/qnetworkinterface_unix.cpp -+++ y/qtbase/src/network/kernel/qnetworkinterface_unix.cpp -@@ -463,8 +463,10 @@ static QNetworkInterface::InterfaceType probeIfType(int socket, int iftype, stru - case IFM_ETHER: - return QNetworkInterface::Ethernet; - -+#ifndef Q_OS_FREEBSD - case IFM_FDDI: - return QNetworkInterface::Fddi; -+#endif - - case IFM_IEEE80211: - return QNetworkInterface::Ieee80211; --- -2.20.1 - diff --git a/patches/qt-configure-detect-fxc.exe-more-thoroughly.patch b/patches/qt-configure-detect-fxc.exe-more-thoroughly.patch deleted file mode 100644 index 831bd65..0000000 --- a/patches/qt-configure-detect-fxc.exe-more-thoroughly.patch +++ /dev/null @@ -1,132 +0,0 @@ -From d64ee0c6be7e9ce672382709d8ea38e30d1d5e0d Mon Sep 17 00:00:00 2001 -From: Oliver Wolff -Date: Tue, 12 Dec 2017 20:02:55 +0100 -Subject: configure: detect fxc.exe more thoroughly - -When building ANGLE, we need the shader compiler (fxc.exe), which is not -shipped with MinGW. Previously, we required an installed DirectX SDK. -For Windows versions >= 8, the DX SDK is also part of the Windows Kit, -so we also allow the user to specify the location of the Windows Kit. -We also detect fxc on 64-bit hosts now, and in newer SDK versions which -version the binary directory. - -The detected binary is now exported by configure, so the ANGLE project -file does not need to duplicate the logic anymore. - -Task-number: QTBUG-52487 -Change-Id: I41a17992909041dd84291b69498195cc8b8fab8a ---- - src/angle/src/common/common.pri | 16 +--------------- - src/gui/configure.json | 11 +++-------- - src/gui/configure.pri | 31 +++++++++++++++++++++++++++---- - 3 files changed, 31 insertions(+), 27 deletions(-) - -diff --git x/qtbase/src/angle/src/common/common.pri y/qtbase/src/angle/src/common/common.pri -index c1fad14951..2c0af02b58 100644 ---- x/qtbase/src/angle/src/common/common.pri -+++ y/qtbase/src/angle/src/common/common.pri -@@ -21,20 +21,6 @@ lib_replace.replace = \$\$\$\$[QT_INSTALL_LIBS] - lib_replace.CONFIG = path - QMAKE_PRL_INSTALL_REPLACE += lib_replace - --# DirectX is included in the Windows 8 Kit, but everything else requires the DX SDK. --winrt|msvc { -- FXC = fxc.exe --} else { -- DX_DIR = $$(DXSDK_DIR) -- isEmpty(DX_DIR) { -- error("Cannot determine DirectX SDK location. Please set DXSDK_DIR environment variable.") -- } -- -- equals(QMAKE_TARGET.arch, x86_64) { -- FXC = \"$${DX_DIR}Utilities\\bin\\x64\\fxc.exe\" -- } else { -- FXC = \"$${DX_DIR}Utilities\\bin\\x86\\fxc.exe\" -- } --} -+FXC = $$shell_quote($$shell_path($$QMAKE_FXC_LOCATION)) - - static: DEFINES *= LIBGLESV2_EXPORT_H_ ANGLE_EXPORT= -diff --git x/qtbase/src/gui/configure.json y/qtbase/src/gui/configure.json -index 4145ceddf6..2a96bc5a75 100644 ---- x/qtbase/src/gui/configure.json -+++ y/qtbase/src/gui/configure.json -@@ -702,10 +702,6 @@ - } - }, - -- "testTypeAliases": { -- "files": [ "fxc" ] -- }, -- - "tests": { - "angle_d3d11_qdtd": { - "label": "D3D11_QUERY_DATA_TIMESTAMP_DISJOINT", -@@ -721,9 +717,7 @@ - "fxc": { - "label": "Direct3D Shader Compiler", - "type": "fxc", -- "files": [ -- "fxc.exe" -- ] -+ "log": "value" - }, - "egl-x11": { - "label": "EGL on X11", -@@ -976,7 +970,8 @@ - "condition": "features.dxguid && tests.fxc && (features.direct3d9 || (config.winrt && features.direct3d11 && libs.d3dcompiler))", - "output": [ - "publicFeature", -- { "type": "define", "name": "QT_OPENGL_ES_2_ANGLE" } -+ { "type": "define", "name": "QT_OPENGL_ES_2_ANGLE" }, -+ { "type": "varAssign", "name": "QMAKE_FXC_LOCATION", "value": "tests.fxc.value" } - ] - }, - "angle_d3d11_qdtd": { -diff --git x/qtbase/src/gui/configure.pri y/qtbase/src/gui/configure.pri -index f53a93063c..bc27a756a3 100644 ---- x/qtbase/src/gui/configure.pri -+++ y/qtbase/src/gui/configure.pri -@@ -20,12 +20,35 @@ defineTest(qtConfLibrary_freetype) { - # DXSDK_DIR variable. Starting with Windows Kit 8, it is included in - # the Windows SDK. - defineTest(qtConfTest_fxc) { -- dxdir = $$getenv("DXSDK_DIR") -- !isEmpty(dxdir) { -- EXTRA_PATH += $$dxdir/Utilities/bin/x86 -+ !mingw { -+ fxc = $$qtConfFindInPath("fxc.exe") -+ } else { -+ equals(QMAKE_HOST.arch, x86_64): \ -+ fns = x64/fxc.exe -+ else: \ -+ fns = x86/fxc.exe -+ dxdir = $$(DXSDK_DIR) -+ !isEmpty(dxdir) { -+ fxc = $$dxdir/Utilities/bin/$$fns -+ } else { -+ winkitbindir = $$(WindowsSdkVerBinPath) -+ !isEmpty(winkitbindir) { -+ fxc = $$winkitbindir/$$fns -+ } else { -+ winkitdir = $$(WindowsSdkDir) -+ !isEmpty(winkitdir): \ -+ fxc = $$winkitdir/bin/$$fns -+ } -+ } - } - -- qtConfTest_files($${1}): return(true) -+ !isEmpty(fxc):exists($$fxc) { -+ $${1}.value = $$clean_path($$fxc) -+ export($${1}.value) -+ $${1}.cache += value -+ export($${1}.cache) -+ return(true) -+ } - return(false) - } - --- -2.17.0 - diff --git a/patches/qt-configure-refactor-directx-checks.patch b/patches/qt-configure-refactor-directx-checks.patch deleted file mode 100644 index 0de8fb3..0000000 --- a/patches/qt-configure-refactor-directx-checks.patch +++ /dev/null @@ -1,442 +0,0 @@ -From 6b73c48ac35de82b95b74f8dd614fe282209cd61 Mon Sep 17 00:00:00 2001 -From: Oswald Buddenhagen -Date: Tue, 12 Dec 2017 12:21:16 +0100 -Subject: configure: refactor directx checks - -properly atomize the libraries and express their dependencies, and -adjust the project files accordingly. - -note that we don't try to use any additional paths, as all SDKs we -currently support have built-in directx 11 support: -- msvc2013 comes with win sdk 8.1; that is also used for win7 targets -- mingw-64 5.3 (though this one is missing fxc, which is why the code - path for using an external sdk for that remains) - -Change-Id: Ib44e389ef46567308293c2bbcad20a96e8ef70c7 ---- - src/angle/src/common/gles_common.pri | 6 +- - src/angle/src/libEGL/libEGL.pro | 5 +- - src/gui/configure.json | 176 ++++++++++++++---- - src/gui/configure.pri | 19 +- - .../fontdatabases/windows/windows.pri | 9 +- - .../fontdatabases/winrt/winrt.pri | 4 +- - src/plugins/platforms/direct2d/direct2d.pro | 3 +- - src/plugins/platforms/platforms.pro | 6 +- - src/plugins/platforms/windows/windows.pri | 2 + - src/plugins/platforms/winrt/winrt.pro | 3 +- - 10 files changed, 168 insertions(+), 65 deletions(-) - -diff --git x/qtbase/src/angle/src/common/gles_common.pri y/qtbase/src/angle/src/common/gles_common.pri -index 82d38a62e6..927949d758 100644 ---- x/qtbase/src/angle/src/common/gles_common.pri -+++ y/qtbase/src/angle/src/common/gles_common.pri -@@ -5,11 +5,11 @@ INCLUDEPATH += $$OUT_PWD/.. $$ANGLE_DIR/src/libANGLE - - # Remember to adapt src/gui/configure.* if the Direct X version changes. - !winrt: \ -- LIBS_PRIVATE += -ld3d9 -+ QMAKE_USE_PRIVATE += d3d9 - winrt: \ -- LIBS_PRIVATE += -ld3dcompiler -ldxgi -ld3d11 -+ QMAKE_USE_PRIVATE += d3dcompiler d3d11 dxgi - --LIBS_PRIVATE += -ldxguid -+QMAKE_USE_PRIVATE += dxguid - - STATICLIBS = translator preprocessor - for(libname, STATICLIBS) { -diff --git x/qtbase/src/angle/src/libEGL/libEGL.pro y/qtbase/src/angle/src/libEGL/libEGL.pro -index 9e9c639002..ad2117f2fc 100644 ---- x/qtbase/src/angle/src/libEGL/libEGL.pro -+++ y/qtbase/src/angle/src/libEGL/libEGL.pro -@@ -1,9 +1,10 @@ - include(../common/common.pri) - DEF_FILE_TARGET = $${TARGET} - TARGET = $$qtLibraryTarget($${LIBEGL_NAME}) --winrt: LIBS_PRIVATE += -ld3d11 -+winrt: QMAKE_USE_PRIVATE += d3d11 -+QMAKE_USE_PRIVATE += dxguid - --LIBS_PRIVATE += -ldxguid -L$$QT_BUILD_TREE/lib -l$$qtLibraryTarget($${LIBGLESV2_NAME}) -+LIBS_PRIVATE += -L$$QT_BUILD_TREE/lib -l$$qtLibraryTarget($${LIBGLESV2_NAME}) - - DEFINES += GL_APICALL= GL_GLEXT_PROTOTYPES= EGLAPI= LIBEGL_IMPLEMENTATION - -diff --git x/qtbase/src/gui/configure.json y/qtbase/src/gui/configure.json -index 219385a108..4145ceddf6 100644 ---- x/qtbase/src/gui/configure.json -+++ y/qtbase/src/gui/configure.json -@@ -65,21 +65,79 @@ - "-lbcm_host" - ] - }, -- "direct2d": { -- "label": "Direct 2D", -- "export": "", -+ "dxguid": { -+ "label": "DirectX GUID", -+ "sources": [ -+ "-ldxguid" -+ ] -+ }, -+ "dxgi": { -+ "label": "DirectX GI", -+ "headers": [ "dxgi.h" ], -+ "sources": [ -+ "-ldxgi" -+ ] -+ }, -+ "dxgi1_2": { -+ "label": "DirectX GI 1.2", - "test": { -- "include": [ "d3d11_1.h", "d2d1_1.h", "d2d1_1helper.h", "dxgi1_2.h", "wrl.h", "dwrite.h" ], -- "tail": "using Microsoft::WRL::ComPtr;", - "main": [ -- "ComPtr d2dFactory;", -- "D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dFactory.ReleaseAndGetAddressOf());", -- "ComPtr surface;", -+ "// fails with mingw-w64 5.4.0 - declaration is missing from header", -+ "IDXGISurface1 *surface;", - "(void) surface;" - ] - }, -+ "headers": [ "dxgi1_2.h" ], -+ "sources": [ -+ "-ldxgi" -+ ] -+ }, -+ "d3d9": { -+ "label": "Direct3D 9", -+ "headers": "d3d9.h", -+ "sources": [ -+ "-ld3d9" -+ ] -+ }, -+ "d3d11": { -+ "label": "Direct3D 11", -+ "headers": "d3d11.h", -+ "sources": [ -+ "-ld3d11" -+ ] -+ }, -+ "d3d11_1": { -+ "label": "Direct3D 11.1", -+ "headers": "d3d11_1.h", -+ "sources": [ -+ "-ld3d11" -+ ] -+ }, -+ "d3dcompiler": { -+ "label": "Direct3D Shader Compiler Library", -+ "headers": "d3dcompiler.h", -+ "sources": [ -+ "-ld3dcompiler" -+ ] -+ }, -+ "d2d1": { -+ "label": "Direct2D 1", -+ "headers": [ "d2d1.h", "d2d1helper.h" ], -+ "sources": [ -+ "-ld2d1" -+ ] -+ }, -+ "d2d1_1": { -+ "label": "Direct2D 1.1", -+ "test": { -+ "main": [ -+ "ID2D1Factory1 *d2dFactory;", -+ "D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dFactory);" -+ ] -+ }, -+ "headers": [ "d2d1_1.h", "d2d1_1helper.h" ], - "sources": [ -- "-ld2d1 -ldwrite -ld3d11" -+ "-ld2d1" - ] - }, - "directfb": { -@@ -96,9 +154,8 @@ - { "type": "pkgConfig", "args": "directfb" } - ] - }, -- "directwrite": { -+ "dwrite": { - "label": "DirectWrite", -- "export": "", - "test": { - "include": [ "dwrite.h", "d2d1.h" ], - "main": [ -@@ -107,6 +164,29 @@ - " (IUnknown **)(&factory));" - ] - }, -+ "headers": "dwrite.h", -+ "sources": [ -+ "-ldwrite" -+ ] -+ }, -+ "dwrite_1": { -+ "label": "DirectWrite 1", -+ "headers": "dwrite_1.h", -+ "sources": [ -+ "-ldwrite" -+ ] -+ }, -+ "dwrite_2": { -+ "label": "DirectWrite 2", -+ "test": { -+ "main": [ -+ "IUnknown *factory = 0;", -+ "(void)(size_t(DWRITE_E_NOCOLOR) + sizeof(IDWriteFontFace2));", -+ "DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2),", -+ " &factory);" -+ ] -+ }, -+ "headers": "dwrite_2.h", - "sources": [ - "-ldwrite" - ] -@@ -623,7 +703,7 @@ - }, - - "testTypeAliases": { -- "files": [ "directX" ] -+ "files": [ "fxc" ] - }, - - "tests": { -@@ -638,26 +718,10 @@ - ] - } - }, -- "directwrite2": { -- "label": "DirectWrite 2", -- "type": "compile", -- "test": { -- "include": [ "dwrite_2.h", "d2d1.h" ], -- "main": [ -- "IUnknown *factory = 0;", -- "(void)(size_t(DWRITE_E_NOCOLOR) + sizeof(IDWriteFontFace2));", -- "DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2),", -- " &factory);" -- ] -- }, -- "use": "directwrite" -- }, -- "directx": { -- "label": "DirectX SDK", -- "type": "directX", -+ "fxc": { -+ "label": "Direct3D Shader Compiler", -+ "type": "fxc", - "files": [ -- "d3dcompiler.h", -- "d3d11.lib", - "fxc.exe" - ] - }, -@@ -909,7 +973,7 @@ - "angle": { - "label": "ANGLE", - "autoDetect": "features.opengles2 || features.opengl-dynamic", -- "condition": "config.win32 && tests.directx", -+ "condition": "features.dxguid && tests.fxc && (features.direct3d9 || (config.winrt && features.direct3d11 && libs.d3dcompiler))", - "output": [ - "publicFeature", - { "type": "define", "name": "QT_OPENGL_ES_2_ANGLE" } -@@ -936,19 +1000,59 @@ - "directwrite": { - "label": "DirectWrite", - "emitIf": "config.win32", -- "condition": "libs.directwrite", -+ "condition": "libs.dwrite", -+ "output": [ "privateFeature" ] -+ }, -+ "directwrite1": { -+ "label": "DirectWrite 1", -+ "emitIf": "config.win32", -+ "condition": "libs.dwrite_1", - "output": [ "privateFeature" ] - }, - "directwrite2": { - "label": "DirectWrite 2", - "emitIf": "config.win32", -- "condition": "features.directwrite && tests.directwrite2", -+ "condition": "features.directwrite1 && libs.dwrite_2", -+ "output": [ "privateFeature" ] -+ }, -+ "dxguid": { -+ "label": "DirectX GUID", -+ "condition": "config.win32 && libs.dxguid", -+ "output": [ "privateFeature" ] -+ }, -+ "direct3d9": { -+ "label": "Direct 3D 9", -+ "condition": "config.win32 && !config.winrt && libs.d3d9", -+ "output": [ "privateFeature" ] -+ }, -+ "dxgi": { -+ "label": "DirectX GI", -+ "condition": "config.win32 && libs.dxgi", -+ "output": [ "privateFeature" ] -+ }, -+ "dxgi1_2": { -+ "label": "DirectX GI 1.2", -+ "condition": "features.dxgi && libs.dxgi1_2", -+ "output": [ "privateFeature" ] -+ }, -+ "direct3d11": { -+ "label": "Direct 3D 11", -+ "condition": "features.dxgi && libs.d3d11", -+ "output": [ "privateFeature" ] -+ }, -+ "direct3d11_1": { -+ "label": "Direct 3D 11.1", -+ "condition": "features.direct3d11 && features.dxgi1_2 && libs.d3d11_1", - "output": [ "privateFeature" ] - }, - "direct2d": { - "label": "Direct 2D", -- "section": "Platform plugins", -- "condition": "config.win32 && !config.winrt && libs.direct2d", -+ "condition": "config.win32 && !config.winrt && features.direct3d11 && libs.d2d1", -+ "output": [ "privateFeature" ] -+ }, -+ "direct2d1_1": { -+ "label": "Direct 2D 1.1", -+ "condition": "features.direct2d && libs.d2d1_1", - "output": [ "privateFeature" ] - }, - "evdev": { -diff --git x/qtbase/src/gui/configure.pri y/qtbase/src/gui/configure.pri -index fcd2d1f73e..f53a93063c 100644 ---- x/qtbase/src/gui/configure.pri -+++ y/qtbase/src/gui/configure.pri -@@ -15,29 +15,16 @@ defineTest(qtConfLibrary_freetype) { - return(true) - } - --# Check for Direct X SDK (include, lib, and direct shader compiler 'fxc'). -+# Check for Direct X shader compiler 'fxc'. - # Up to Direct X SDK June 2010 and for MinGW, this is pointed to by the - # DXSDK_DIR variable. Starting with Windows Kit 8, it is included in --# the Windows SDK. Checking for the header is not sufficient, since it --# is also present in MinGW. --defineTest(qtConfTest_directX) { -+# the Windows SDK. -+defineTest(qtConfTest_fxc) { - dxdir = $$getenv("DXSDK_DIR") - !isEmpty(dxdir) { -- EXTRA_INCLUDEPATH += $$dxdir/include -- equals(QT_ARCH, x86_64): \ -- EXTRA_LIBDIR += $$dxdir/lib/x64 -- else: \ -- EXTRA_LIBDIR += $$dxdir/lib/x86 - EXTRA_PATH += $$dxdir/Utilities/bin/x86 - } - -- $$qtConfEvaluate("features.sse2") { -- ky = $$size($${1}.files._KEYS_) -- $${1}.files._KEYS_ += $$ky -- # Not present on MinGW-32 -- $${1}.files.$${ky} = "intrin.h" -- } -- - qtConfTest_files($${1}): return(true) - return(false) - } -diff --git x/qtbase/src/platformsupport/fontdatabases/windows/windows.pri y/qtbase/src/platformsupport/fontdatabases/windows/windows.pri -index 0e64084cf1..9c529f55ea 100644 ---- x/qtbase/src/platformsupport/fontdatabases/windows/windows.pri -+++ y/qtbase/src/platformsupport/fontdatabases/windows/windows.pri -@@ -15,9 +15,14 @@ qtConfig(freetype) { - HEADERS += $$PWD/qwindowsfontdatabase_ft_p.h - } - --qtConfig(directwrite) { -- qtConfig(directwrite2): \ -+qtConfig(directwrite):qtConfig(direct2d) { -+ qtConfig(directwrite2) { -+ QMAKE_USE_PRIVATE += dwrite_2 - DEFINES *= QT_USE_DIRECTWRITE2 -+ } else { -+ QMAKE_USE_PRIVATE += dwrite -+ } -+ QMAKE_USE_PRIVATE += d2d1 - - SOURCES += $$PWD/qwindowsfontenginedirectwrite.cpp - HEADERS += $$PWD/qwindowsfontenginedirectwrite_p.h -diff --git x/qtbase/src/platformsupport/fontdatabases/winrt/winrt.pri y/qtbase/src/platformsupport/fontdatabases/winrt/winrt.pri -index 291ada220f..7617df2e7a 100644 ---- x/qtbase/src/platformsupport/fontdatabases/winrt/winrt.pri -+++ y/qtbase/src/platformsupport/fontdatabases/winrt/winrt.pri -@@ -8,4 +8,6 @@ HEADERS += \ - - DEFINES += __WRL_NO_DEFAULT_LIB__ - --LIBS += -lws2_32 -ldwrite -+LIBS += -lws2_32 -+ -+QMAKE_USE_PRIVATE += dwrite_1 -diff --git x/qtbase/src/plugins/platforms/direct2d/direct2d.pro y/qtbase/src/plugins/platforms/direct2d/direct2d.pro -index 3bfd02bdc8..9764272632 100644 ---- x/qtbase/src/plugins/platforms/direct2d/direct2d.pro -+++ y/qtbase/src/plugins/platforms/direct2d/direct2d.pro -@@ -8,7 +8,8 @@ QT += \ - qtConfig(accessibility): QT += accessibility_support-private - qtConfig(vulkan): QT += vulkan_support-private - --LIBS += -ldwmapi -ld2d1 -ld3d11 -ldwrite -lversion -lgdi32 -+LIBS += -ldwmapi -lversion -lgdi32 -+QMAKE_USE_PRIVATE += dwrite_1 d2d1_1 d3d11_1 dxgi1_2 - - include(../windows/windows.pri) - -diff --git x/qtbase/src/plugins/platforms/platforms.pro y/qtbase/src/plugins/platforms/platforms.pro -index e61887618f..b70d8d5996 100644 ---- x/qtbase/src/plugins/platforms/platforms.pro -+++ y/qtbase/src/plugins/platforms/platforms.pro -@@ -14,10 +14,10 @@ qtConfig(xcb) { - uikit:!watchos: SUBDIRS += ios - osx: SUBDIRS += cocoa - --win32:!winrt: SUBDIRS += windows --winrt: SUBDIRS += winrt -+win32:!winrt:qtConfig(direct3d9): SUBDIRS += windows -+winrt:qtConfig(direct3d11): SUBDIRS += winrt - --qtConfig(direct2d) { -+qtConfig(direct3d11_1):qtConfig(direct2d1_1):qtConfig(directwrite1) { - SUBDIRS += direct2d - } - -diff --git x/qtbase/src/plugins/platforms/windows/windows.pri y/qtbase/src/plugins/platforms/windows/windows.pri -index f4c396f7c5..2f244d8b71 100644 ---- x/qtbase/src/plugins/platforms/windows/windows.pri -+++ y/qtbase/src/plugins/platforms/windows/windows.pri -@@ -9,6 +9,8 @@ mingw: LIBS *= -luuid - # For the dialog helpers: - LIBS += -lshlwapi -lshell32 -ladvapi32 - -+QMAKE_USE_PRIVATE += d3d9/nolink -+ - DEFINES *= QT_NO_CAST_FROM_ASCII - - SOURCES += \ -diff --git x/qtbase/src/plugins/platforms/winrt/winrt.pro y/qtbase/src/plugins/platforms/winrt/winrt.pro -index 042b270cff..fc70eba979 100644 ---- x/qtbase/src/plugins/platforms/winrt/winrt.pro -+++ y/qtbase/src/plugins/platforms/winrt/winrt.pro -@@ -8,7 +8,8 @@ QT += \ - - DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ - --LIBS += -lws2_32 -ld3d11 -+LIBS += -lws2_32 -+QMAKE_USE_PRIVATE += d3d11 - - SOURCES = \ - main.cpp \ --- -2.17.0 - diff --git a/patches/qt-disable-designer.patch b/patches/qt-disable-designer.patch deleted file mode 100644 index 4e303ad..0000000 --- a/patches/qt-disable-designer.patch +++ /dev/null @@ -1,36 +0,0 @@ -diff --git x/qttools/src/linguist/linguist.pro y/qttools/src/linguist/linguist.pro -index 103336da..2fe9656d 100644 ---- x/qttools/src/linguist/linguist.pro -+++ y/qttools/src/linguist/linguist.pro -@@ -3,11 +3,6 @@ SUBDIRS = \ - lrelease \ - lupdate \ - lconvert --!no-png:qtHaveModule(widgets):qtConfig(process): SUBDIRS += linguist -- --qtNomakeTools( \ -- linguist \ --) - - equals(QMAKE_HOST.os, Windows): CMAKE_BIN_SUFFIX = ".exe" - -diff --git x/qttools/src/src.pro y/qttools/src/src.pro -index 41064a5d..fcef4dd8 100644 ---- x/qttools/src/src.pro -+++ y/qttools/src/src.pro -@@ -3,12 +3,6 @@ TEMPLATE = subdirs - qtHaveModule(widgets) { - no-png { - message("Some graphics-related tools are unavailable without PNG support") -- } else { -- SUBDIRS = assistant \ -- pixeltool \ -- designer -- -- linguist.depends = designer - } - } - --- -2.14.3 - diff --git a/patches/qt-fix-macOS-no-printer.patch b/patches/qt-fix-macOS-no-printer.patch deleted file mode 100644 index f336acb..0000000 --- a/patches/qt-fix-macOS-no-printer.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- x/qtbase/src/plugins/platforms/cocoa/qprintengine_mac_p.h -+++ y/qtbase/src/plugins/platforms/cocoa/qprintengine_mac_p.h -@@ -52,6 +52,7 @@ - // - - #include -+#include - - #ifndef QT_NO_PRINTER - ---- x/qtbase/src/plugins/plugins.pro -+++ y/qtbase/src/plugins/plugins.pro -@@ -9,6 +9,3 @@ - !android:qtConfig(library): SUBDIRS *= generic - } - qtHaveModule(widgets): SUBDIRS += styles -- --!winrt:qtHaveModule(printsupport): \ -- SUBDIRS += printsupport diff --git a/resources/ausweisapp.qrc b/resources/ausweisapp.qrc index a845aba..1448405 100644 --- a/resources/ausweisapp.qrc +++ b/resources/ausweisapp.qrc @@ -15,19 +15,9 @@ images/icon_ok.png images/icon_cancelled.png images/Icon_Checked.svg - images/search.svg images/cancel.svg - images/iOS/tabBar/Anbieter-off.png - images/iOS/tabBar/Anbieter-on.png - images/iOS/tabBar/Ausweisen-off.png - images/iOS/tabBar/Ausweisen-on.png - images/iOS/tabBar/Pin-off.png - images/iOS/tabBar/Pin-on.png - images/iOS/tabBar/Verlauf-off.png - images/iOS/tabBar/Verlauf-on.png + images/check.svg images/icon_Pin.svg - images/iOS/tabBar/More-off.svg - images/iOS/tabBar/More-on.svg images/status_error.svg images/status_info.svg images/status_ok.svg @@ -38,18 +28,20 @@ images/ausweis.png images/provider/information.svg images/provider/purpose.svg - images/provider/adresse.png - images/provider/+tablet/adresse.png - images/provider/mail.png - images/provider/+tablet/mail.png - images/provider/telefon.png - images/provider/+tablet/telefon.png - images/provider/url.png - images/provider/+tablet/url.png + images/provider/adresse.svg + images/provider/mail.svg + images/provider/telefon.svg + images/provider/url.svg images/provider/gradient-citizen.png images/provider/gradient-insurance.png images/provider/gradient-finance.png images/provider/gradient-other.png + images/provider/categoryIcons/general.svg + images/provider/categoryIcons/citizen.svg + images/provider/categoryIcons/finance.svg + images/provider/categoryIcons/insurance.svg + images/provider/categoryIcons/other.svg + images/provider/categoryIcons/+android/general_bg.svg images/provider/categoryIcons/+android/general.svg images/provider/categoryIcons/+android/citizen.svg images/provider/categoryIcons/+android/finance.svg @@ -73,7 +65,14 @@ updatable-files/supported-providers.json images/icon_nfc.svg images/icon_remote.svg + images/icon_remote_inactive.svg + images/icon_remote_0.svg + images/icon_remote_25.svg + images/icon_remote_50.svg + images/icon_remote_75.svg + images/icon_remote_100.svg images/icon_bluetooth.svg + images/phone_to_pc.svg images/phone_nfc.svg images/phone_remote.svg images/phone_bluetooth.svg @@ -82,5 +81,11 @@ images/siteWithLogo.png images/icon_pair.svg images/icon_settings.svg + images/trash_icon_white.svg + images/trash_icon.svg + images/icon_save.svg + images/info.svg + images/info_filled.svg + images/triangle.svg diff --git a/resources/ausweisapp_desktop.qrc b/resources/ausweisapp_desktop.qrc index 8a439b1..9fbe502 100644 --- a/resources/ausweisapp_desktop.qrc +++ b/resources/ausweisapp_desktop.qrc @@ -13,16 +13,33 @@ images/start_nPA_eAT.png images/busy_animation.gif images/html_message_section.jpg - images/desktop/background.png + images/search_icon.svg + images/search_cancel.svg + images/desktop/bell_white.svg + images/desktop/bell_green.svg + images/desktop/bell_red.svg + images/desktop/id_card.png + images/desktop/pin-letter-page1.png + images/desktop/pin-letter-page2.png images/desktop/help_icon.svg images/desktop/main_history.svg images/desktop/main_identify.svg + images/desktop/main_info.svg images/desktop/main_pin.svg images/desktop/main_provider.svg images/desktop/sandglass.svg images/desktop/settings_icon.svg images/desktop/titlebar_arrow.svg images/desktop/continue_arrow.svg + images/desktop/info_questions.svg + images/desktop/info_application_log.svg + images/desktop/info_diagnosis.svg + images/desktop/info_license.svg + images/desktop/info_manual.svg + images/desktop/info_rate_application.svg + images/desktop/info_report_error.svg + images/desktop/info_setup.svg + images/desktop/info_version.svg images/randompin/btn_normal_0.png images/randompin/btn_normal_1.png images/randompin/btn_normal_2.png @@ -37,6 +54,7 @@ images/randompin/btn_ok.png images/randompin/btn_cancel.png images/randompin/btn_clear.png + images/reader/default_card_position.png images/reader/default_more_reader.png images/reader/default_no_card.png images/reader/default_no_reader.png diff --git a/resources/ausweisapp_mobile.qrc b/resources/ausweisapp_mobile.qrc index 6b29817..11659c3 100644 --- a/resources/ausweisapp_mobile.qrc +++ b/resources/ausweisapp_mobile.qrc @@ -2,7 +2,7 @@ qtquickcontrols2.conf images/zahnraeder.svg - images/check.svg + images/arrowLeft.svg images/arrowRight.svg images/share.svg images/android/navigation/ausweisen.svg @@ -15,16 +15,19 @@ images/android/navigation/support.svg images/android/navigation/bewerten.svg images/android/navigation/tutorial.svg + images/android/search_icon.svg images/iOS/search_icon.svg images/iOS/search_cancel.svg + images/iOS/more/icon_mehr_npa.svg images/iOS/more/icon_mehr_favorit.svg images/iOS/more/icon_mehr_fragen.svg images/iOS/more/icon_mehr_info.svg images/iOS/more/icon_mehr_license.svg - images/iOS/more/icon_mehr_remotereader.svg + images/iOS/more/icon_mehr_verlauf.svg images/iOS/more/icon_mehr_upload.svg images/iOS/more/icon_mehr_log.svg images/iOS/more/icon_mehr_tutorial.svg + images/iOS/more/icon_mehr_report.svg images/tutorial/main_menu_what_caret.svg images/tutorial/main_menu_where_caret.svg images/tutorial/main_menu_how_caret.svg @@ -80,27 +83,37 @@ images/tutorial/user-tine@3x.png images/tutorial/providericons.png images/tutorial/play_movie.png - images/tutorial/screenshot_cert_de.png - images/tutorial/screenshot_cert_en.png - images/tutorial/screenshot_providerlist_de.png - images/tutorial/screenshot_providerlist_en.png - images/tutorial/screenshot_menu_providerlist_de.png - images/tutorial/screenshot_menu_providerlist_en.png + images/tutorial/screenshot_cert_android_de.png + images/tutorial/screenshot_cert_android_en.png + images/tutorial/screenshot_cert_ios_de.png + images/tutorial/screenshot_cert_ios_en.png + images/tutorial/screenshot_providerlist_android_de.png + images/tutorial/screenshot_providerlist_android_en.png + images/tutorial/screenshot_providerlist_ios_de.png + images/tutorial/screenshot_providerlist_ios_en.png + images/tutorial/screenshot_remoteservice_ios_en.png + images/tutorial/screenshot_remoteservice_ios_de.png + images/tutorial/screenshot_menu_providerlist_android_de.png + images/tutorial/screenshot_menu_providerlist_android_en.png images/tutorial/screenshot_pairing_de.png images/tutorial/screenshot_pairing_en.png - images/tutorial/screenshot_sac_menu_de.png - images/tutorial/screenshot_sac_menu_en.png - images/tutorial/screenshot_choose_reader_de.png - images/tutorial/screenshot_choose_reader_en.png - images/tutorial/screenshot_pin_management_menu_en.png - images/tutorial/screenshot_pin_management_menu_de.png + images/tutorial/screenshot_sac_menu_android_de.png + images/tutorial/screenshot_sac_menu_android_en.png + images/tutorial/screenshot_choose_reader_android_de.png + images/tutorial/screenshot_choose_reader_android_en.png + images/tutorial/screenshot_choose_reader_ios_de.png + images/tutorial/screenshot_choose_reader_ios_en.png + images/tutorial/screenshot_pin_management_menu_android_en.png + images/tutorial/screenshot_pin_management_menu_android_de.png + images/tutorial/screenshot_pin_management_menu_ios_en.png + images/tutorial/screenshot_pin_management_menu_ios_de.png images/tutorial/section_seperator_what.svg images/tutorial/section_seperator_where.svg images/tutorial/section_seperator_how.svg images/tutorial/section_seperator_important.svg images/tutorial/generated/where_overview_question.svg - images/tutorial/generated/where_providerlist_screenshot_de.svg - images/tutorial/generated/where_providerlist_screenshot_en.svg + images/tutorial/generated/where_providerlist_screenshot_android_de.svg + images/tutorial/generated/where_providerlist_screenshot_android_en.svg images/tutorial/generated/where_identify_now_de.svg images/tutorial/generated/where_identify_now_en.svg images/tutorial/generated/where_userdata_example_de.svg @@ -129,18 +142,14 @@ images/tutorial/generated/reader_sac_provider_on_laptop.svg images/tutorial/generated/reader_sac_npa_on_laptop.svg images/tutorial/generated/reader_sac_aa2_ok.svg - images/tutorial/generated/reader_sac_menu_de.svg - images/tutorial/generated/reader_sac_menu_en.svg + images/tutorial/generated/reader_sac_menu_android_de.svg + images/tutorial/generated/reader_sac_menu_android_en.svg + images/tutorial/generated/reader_sac_menu_ios_de.svg + images/tutorial/generated/reader_sac_menu_ios_en.svg images/tutorial/generated/reader_sac_no_nfc_devices.svg images/tutorial/generated/reader_sac_no_nfc_provider.svg images/tutorial/generated/reader_bluetooth_card_inserted.svg images/tutorial/generated/reader_bluetooth_connection.svg - images/provider/categoryIcons/general.svg - images/provider/categoryIcons/citizen.svg - images/provider/categoryIcons/finance.svg - images/provider/categoryIcons/insurance.svg - images/provider/categoryIcons/other.svg - images/provider/categoryIcons/+android/general_bg.svg images/provider/categoryIcons/+android/general_button.svg images/provider/categoryIcons/+android/citizen_bg.svg images/provider/categoryIcons/+android/citizen_button.svg @@ -150,10 +159,12 @@ images/provider/categoryIcons/+android/insurance_button.svg images/provider/categoryIcons/+android/other_bg.svg images/provider/categoryIcons/+android/other_button.svg - images/phone_to_pc.svg images/android/navigation/remotesettings.svg - images/trash_icon.svg images/trash_icon_all.svg - images/trash_icon_white.svg + images/iOS/tabBar/anbieter.svg + images/iOS/tabBar/ausweisen.svg + images/iOS/tabBar/more.svg + images/iOS/tabBar/pin.svg + images/iOS/tabBar/remoteleser.svg diff --git a/resources/config.json.in b/resources/config.json.in index a816277..a9856fe 100644 --- a/resources/config.json.in +++ b/resources/config.json.in @@ -22,6 +22,7 @@ "DETESTeID00002_DETESTeID00004", "DETESTeID00001_DETESTeID00002", "DETESTeID00001", + "DECVCAeIDCTL0402_DECVCAeIDCTL0403", "DECVCAeIDCTL0401_DECVCAeIDCTL0402", "DECVCAeIDCT00001_DECVCAeIDCTL0401", "DECVCAeIDCT00001_DECVCAeIDCT00001" @@ -32,9 +33,10 @@ "7F218201B67F4E82016E5F290100420E44455445535465494430303030327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410474FF63AB838C73C303AC003DFEE95CF8BF55F91E8FEBCB7395D942036E47CF1845EC786EC95BB453AAC288AD023B6067913CF9B63F908F49304E5CFC8B3050DD8701015F200E44455445535465494430303030347F4C12060904007F0007030102025305FC0F13FFFF5F25060102000501015F24060105000501015F37405C035A0611B6C58F0B5261FDD009DECAB7DC7A79482D5248CCA119059B7D82B2157CF0C4A499BCF441EFDD35E294A58C0AF19A34A0762159533285ACF170A505", "7F218201B67F4E82016E5F290100420E44455445535465494430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104096EB58BFD86252238EC2652185C43C3A56C320681A21E37A8E69DDC387C0C5F5513856EFE2FDC656E604893212E29449B365E304605AC5413E75BE31E641F128701015F200E44455445535465494430303030327F4C12060904007F0007030102025305FE0F01FFFF5F25060100000902015F24060103000902015F3740141120A0FDFC011A52F3F72B387A3DC7ACA88B4868D5AE9741780B6FF8A0B49E5F55169A2D298EF5CF95935DCA0C3DF3E9D42DC45F74F2066317154961E6C746", "7F218201B67F4E82016E5F290100420E44455445535465494430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104184BB519FC2A8F52DC0DC73112FACFE914F2A49B678DD5799A2B1DFE95E1A66359014E22FA8D66438413CEBA6CF0E215576B673376BF617AF4DFE9761D2290148701015F200E44455445535465494430303030317F4C12060904007F0007030102025305FE0F01FFFF5F25060100000801035F24060103000801035F37409F25EBFAF4B91E4C60A1683754C5DC076A3179753EF97D9F8CB01FE1DCD3B8C83E7A26602AB1F344BE5706006D79A9FF6A9716404DC83B9F30E1213B393128A2", - "7F218201BA7F4E8201725F290100421044454356434165494443544C303430317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641043EE15015916563E31459045924DE804C1D93A77652AA25D0B753730DBA3233886A9A9B28A06AF84CC5A40F78E9167CA40B8098724A3A0332283D0A52C5453FE08701015F201044454356434165494443544C303430327F4C12060904007F0007030102025305FE1FFFFFFF5F25060106000300015F24060109000300015F374068261CEB4DC915301371C9B273377F33CEB25AFA07E70EDB3498ACF0327DC13B9AF99A9D694A6D048F0DB6FF1774E882CA8F41C8A0B96FDDF6F6DDABFD55CB43", - "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641042C037C6CF8C0B62E36E220B7D411404AF248A2C83C569A49FAB02FC232D2395B3A5FF80DDB01D0DFAFCEF55E54ACCBA4C56E528F0746BEF1108E7D9B0122EBA78701015F201044454356434165494443544C303430317F4C12060904007F0007030102025305FE1FFFFFFF5F25060106000300015F24060109000300015F37404FB2F9F17D38656EEC2846CBB1711E07D739E6A584D487B3AFBA5C723C73A10236995E6A19499DE941DFE3CC044E2AACB6B5869C0E46C9585AEF892E2236F62E", - "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104206160B85B82B2BE249DCD7B15FD1AD46CA03F39FE675C07535806E57CE24349392BBB9F73B364672F12243F18083DCC49ACA613767F0873AEB60715FC605A288701015F2010444543564341654944435430303030317F4C12060904007F0007030102025305FE1FFFFFFF5F25060106000300015F24060109000300015F37402B62927D46BF675DB387FC4A425FF3B604B20DD5ED6FFCDAFA9DA1DD920DC996245B358167C66A721DF39C6897864E4D648746339A97D22D08659885F92BDE21" + "7F218201BA7F4E8201725F290100421044454356434165494443544C303430327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641040DC10C2E105D1CFC84CCDAB7AC0A37F161234D9F5549D83FD29E1D3B3A1455DC5F5464FE69E7B1F18F57AD6A0BBB3B2F673A356B3AC8AB525A96A78E71EC20B38701015F201044454356434165494443544C303430337F4C12060904007F0007030102025305FE1FFFFFFF5F25060108000900025F24060201000900015F37407D738AA07BBC24E4969ABC424CC75B1BBE9F4A8AEB497544516F32D2284A6F785BFC630BE9C6D96C6DE8FFF377FE68A3950DE140953DEFAFADA92A201774C0BB", + "7F218201BA7F4E8201725F290100421044454356434165494443544C303430317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641044EDF23187A2E4535AB7AA3C29AD43E3ACFB7C38230CCC7B705829E60DE14946D7A0257527E1488D9EBB967D5734FCBCFBA3A1085798AFA7883FDAF88605A46008701015F201044454356434165494443544C303430327F4C12060904007F0007030102025305FE1FFFFFFF5F25060108000900025F24060201000900015F3740A2CC27745E141CE871645178F5D8B55B9ECAA690A7975EF7AB518145D9CE9FA31C326F923EDDC6AD58CF0E644CDF86FC2EAF4D059B0598C2CEB6C713D825CD69", + "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641048A50D5902E9E63D103974E9FBE1D7E61BA186585E2A12A4643FFA57DFF0613FE213A7C3793C8C5C6C095224DDCA409AFE92998E3F372D826AD008FC6A0981DE18701015F201044454356434165494443544C303430317F4C12060904007F0007030102025305FE1FFFFFFF5F25060108000900025F24060201000900015F3740767C88881693C2AA936EC55FC0104EFF92FAD1AB9B1CF26ECCFA49AE98E9F9EA5BDD60AFFA6FFC6526AFE47BB6897E67241F2170A51F635EBC8AA8414748562C", + "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641041A47FE0DA706867F877AB071E8867778EDB6E7C12B13FA13ED93D54C86A44E3BA9F93D1B978B54841202B14A924BBA1A56C76B9E596614C66A9D2CBCDC8B29B18701015F2010444543564341654944435430303030317F4C12060904007F0007030102025305FE1FFFFFFF5F25060108000900025F24060201000900015F37407CDC52ADB660CC0EB56841EF9D5773DDE84A5581853CCF3DCF24D555C2FDE8F31D2128B5D6A0A528BF07CFDA005DBCACFFCDB14755BB132E47CFA0E34233A290" ], "_comment_5": "array of certificates for checking the authenticity of updates; DER format, hex encoded", @@ -151,18 +153,20 @@ "minStaticKeySizes": { "Rsa": 2000, "Dsa": 2000, + "Dh": 2000, "Ec": 224 }, "minEphemeralKeySizes": { "Rsa": 2000, "Dsa": 2000, + "Dh": 2000, "Ec": 224 }, "selfAuthentication": { - "_comment_1": "TCTokenURL for self authentication (AusweisAuskunft)", + "_comment_1": "TCTokenURL for self-authentication (AusweisAuskunft)", "url": "https://www.autentapp.de/AusweisAuskunft/WebServiceRequesterServlet?mode=json", "testUrl": "https://test.governikus-eid.de/AusweisAuskunft/WebServiceRequesterServlet?mode=json" }, diff --git a/resources/images/android/hdpi/background_npa.png b/resources/images/android/hdpi/background_npa.png new file mode 100644 index 0000000..6ff0210 Binary files /dev/null and b/resources/images/android/hdpi/background_npa.png differ diff --git a/resources/images/android/hdpi/foreground_npa.png b/resources/images/android/hdpi/foreground_npa.png new file mode 100644 index 0000000..df85de4 Binary files /dev/null and b/resources/images/android/hdpi/foreground_npa.png differ diff --git a/resources/images/android/hdpi/foreground_npa_beta.png b/resources/images/android/hdpi/foreground_npa_beta.png new file mode 100644 index 0000000..e755bb9 Binary files /dev/null and b/resources/images/android/hdpi/foreground_npa_beta.png differ diff --git a/resources/images/android/hdpi/foreground_npa_preview.png b/resources/images/android/hdpi/foreground_npa_preview.png new file mode 100644 index 0000000..ece6b8e Binary files /dev/null and b/resources/images/android/hdpi/foreground_npa_preview.png differ diff --git a/resources/images/android/hdpi/npa.png b/resources/images/android/hdpi/npa.png index d6b982b..c75d6b7 100644 Binary files a/resources/images/android/hdpi/npa.png and b/resources/images/android/hdpi/npa.png differ diff --git a/resources/images/android/hdpi/npa_beta.png b/resources/images/android/hdpi/npa_beta.png index 7c38a02..210069e 100644 Binary files a/resources/images/android/hdpi/npa_beta.png and b/resources/images/android/hdpi/npa_beta.png differ diff --git a/resources/images/android/hdpi/npa_preview.png b/resources/images/android/hdpi/npa_preview.png index 6c1a5a4..f5e7a19 100644 Binary files a/resources/images/android/hdpi/npa_preview.png and b/resources/images/android/hdpi/npa_preview.png differ diff --git a/resources/images/android/ldpi/background_npa.png b/resources/images/android/ldpi/background_npa.png new file mode 100644 index 0000000..5e373b6 Binary files /dev/null and b/resources/images/android/ldpi/background_npa.png differ diff --git a/resources/images/android/ldpi/foreground_npa.png b/resources/images/android/ldpi/foreground_npa.png new file mode 100644 index 0000000..81c0360 Binary files /dev/null and b/resources/images/android/ldpi/foreground_npa.png differ diff --git a/resources/images/android/ldpi/foreground_npa_beta.png b/resources/images/android/ldpi/foreground_npa_beta.png new file mode 100644 index 0000000..923da0b Binary files /dev/null and b/resources/images/android/ldpi/foreground_npa_beta.png differ diff --git a/resources/images/android/ldpi/foreground_npa_preview.png b/resources/images/android/ldpi/foreground_npa_preview.png new file mode 100644 index 0000000..4493411 Binary files /dev/null and b/resources/images/android/ldpi/foreground_npa_preview.png differ diff --git a/resources/images/android/ldpi/npa.png b/resources/images/android/ldpi/npa.png index 4deb765..7970e41 100644 Binary files a/resources/images/android/ldpi/npa.png and b/resources/images/android/ldpi/npa.png differ diff --git a/resources/images/android/ldpi/npa_beta.png b/resources/images/android/ldpi/npa_beta.png index 82ac719..c9697b4 100644 Binary files a/resources/images/android/ldpi/npa_beta.png and b/resources/images/android/ldpi/npa_beta.png differ diff --git a/resources/images/android/ldpi/npa_preview.png b/resources/images/android/ldpi/npa_preview.png index 8e507b6..2bc0f3c 100644 Binary files a/resources/images/android/ldpi/npa_preview.png and b/resources/images/android/ldpi/npa_preview.png differ diff --git a/resources/images/android/mdpi/background_npa.png b/resources/images/android/mdpi/background_npa.png new file mode 100644 index 0000000..ede7003 Binary files /dev/null and b/resources/images/android/mdpi/background_npa.png differ diff --git a/resources/images/android/mdpi/foreground_npa.png b/resources/images/android/mdpi/foreground_npa.png new file mode 100644 index 0000000..a956059 Binary files /dev/null and b/resources/images/android/mdpi/foreground_npa.png differ diff --git a/resources/images/android/mdpi/foreground_npa_beta.png b/resources/images/android/mdpi/foreground_npa_beta.png new file mode 100644 index 0000000..3b9577f Binary files /dev/null and b/resources/images/android/mdpi/foreground_npa_beta.png differ diff --git a/resources/images/android/mdpi/foreground_npa_preview.png b/resources/images/android/mdpi/foreground_npa_preview.png new file mode 100644 index 0000000..c62fef8 Binary files /dev/null and b/resources/images/android/mdpi/foreground_npa_preview.png differ diff --git a/resources/images/android/mdpi/npa.png b/resources/images/android/mdpi/npa.png index f8a973a..02450ed 100644 Binary files a/resources/images/android/mdpi/npa.png and b/resources/images/android/mdpi/npa.png differ diff --git a/resources/images/android/mdpi/npa_beta.png b/resources/images/android/mdpi/npa_beta.png index daae842..e7759e8 100644 Binary files a/resources/images/android/mdpi/npa_beta.png and b/resources/images/android/mdpi/npa_beta.png differ diff --git a/resources/images/android/mdpi/npa_preview.png b/resources/images/android/mdpi/npa_preview.png index 3f43bda..6aea52d 100644 Binary files a/resources/images/android/mdpi/npa_preview.png and b/resources/images/android/mdpi/npa_preview.png differ diff --git a/resources/images/android/npa.svg b/resources/images/android/npa.svg new file mode 100644 index 0000000..4a943b3 --- /dev/null +++ b/resources/images/android/npa.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/android/npa_background.svg b/resources/images/android/npa_background.svg new file mode 100644 index 0000000..9094ed2 --- /dev/null +++ b/resources/images/android/npa_background.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/android/npa_beta.svg b/resources/images/android/npa_beta.svg new file mode 100644 index 0000000..0a49f29 --- /dev/null +++ b/resources/images/android/npa_beta.svg @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/android/npa_preview.svg b/resources/images/android/npa_preview.svg new file mode 100644 index 0000000..f99cc1b --- /dev/null +++ b/resources/images/android/npa_preview.svg @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PREVIEW + + + + + + + + + + + + + diff --git a/resources/images/search.svg b/resources/images/android/search_icon.svg similarity index 100% rename from resources/images/search.svg rename to resources/images/android/search_icon.svg diff --git a/resources/images/android/xhdpi/background_npa.png b/resources/images/android/xhdpi/background_npa.png new file mode 100644 index 0000000..5822874 Binary files /dev/null and b/resources/images/android/xhdpi/background_npa.png differ diff --git a/resources/images/android/xhdpi/foreground_npa.png b/resources/images/android/xhdpi/foreground_npa.png new file mode 100644 index 0000000..16450d1 Binary files /dev/null and b/resources/images/android/xhdpi/foreground_npa.png differ diff --git a/resources/images/android/xhdpi/foreground_npa_beta.png b/resources/images/android/xhdpi/foreground_npa_beta.png new file mode 100644 index 0000000..e29fa51 Binary files /dev/null and b/resources/images/android/xhdpi/foreground_npa_beta.png differ diff --git a/resources/images/android/xhdpi/foreground_npa_preview.png b/resources/images/android/xhdpi/foreground_npa_preview.png new file mode 100644 index 0000000..1ab6421 Binary files /dev/null and b/resources/images/android/xhdpi/foreground_npa_preview.png differ diff --git a/resources/images/android/xhdpi/npa.png b/resources/images/android/xhdpi/npa.png index 02c2f5c..8b5c911 100644 Binary files a/resources/images/android/xhdpi/npa.png and b/resources/images/android/xhdpi/npa.png differ diff --git a/resources/images/android/xhdpi/npa_beta.png b/resources/images/android/xhdpi/npa_beta.png index e2ca263..a16de0f 100644 Binary files a/resources/images/android/xhdpi/npa_beta.png and b/resources/images/android/xhdpi/npa_beta.png differ diff --git a/resources/images/android/xhdpi/npa_preview.png b/resources/images/android/xhdpi/npa_preview.png index c88dc5f..002ecec 100644 Binary files a/resources/images/android/xhdpi/npa_preview.png and b/resources/images/android/xhdpi/npa_preview.png differ diff --git a/resources/images/android/xxhdpi/background_npa.png b/resources/images/android/xxhdpi/background_npa.png new file mode 100644 index 0000000..58f9578 Binary files /dev/null and b/resources/images/android/xxhdpi/background_npa.png differ diff --git a/resources/images/android/xxhdpi/foreground_npa.png b/resources/images/android/xxhdpi/foreground_npa.png new file mode 100644 index 0000000..f36de1f Binary files /dev/null and b/resources/images/android/xxhdpi/foreground_npa.png differ diff --git a/resources/images/android/xxhdpi/foreground_npa_beta.png b/resources/images/android/xxhdpi/foreground_npa_beta.png new file mode 100644 index 0000000..666f698 Binary files /dev/null and b/resources/images/android/xxhdpi/foreground_npa_beta.png differ diff --git a/resources/images/android/xxhdpi/foreground_npa_preview.png b/resources/images/android/xxhdpi/foreground_npa_preview.png new file mode 100644 index 0000000..a5183eb Binary files /dev/null and b/resources/images/android/xxhdpi/foreground_npa_preview.png differ diff --git a/resources/images/android/xxhdpi/npa.png b/resources/images/android/xxhdpi/npa.png index d4ca03c..a5e83aa 100644 Binary files a/resources/images/android/xxhdpi/npa.png and b/resources/images/android/xxhdpi/npa.png differ diff --git a/resources/images/android/xxhdpi/npa_beta.png b/resources/images/android/xxhdpi/npa_beta.png index 9f19d8c..e3ed6a4 100644 Binary files a/resources/images/android/xxhdpi/npa_beta.png and b/resources/images/android/xxhdpi/npa_beta.png differ diff --git a/resources/images/android/xxhdpi/npa_preview.png b/resources/images/android/xxhdpi/npa_preview.png index 28b65aa..65c5841 100644 Binary files a/resources/images/android/xxhdpi/npa_preview.png and b/resources/images/android/xxhdpi/npa_preview.png differ diff --git a/resources/images/android/xxxhdpi/background_npa.png b/resources/images/android/xxxhdpi/background_npa.png new file mode 100644 index 0000000..fe37593 Binary files /dev/null and b/resources/images/android/xxxhdpi/background_npa.png differ diff --git a/resources/images/android/xxxhdpi/foreground_npa.png b/resources/images/android/xxxhdpi/foreground_npa.png new file mode 100644 index 0000000..85fc9ab Binary files /dev/null and b/resources/images/android/xxxhdpi/foreground_npa.png differ diff --git a/resources/images/android/xxxhdpi/foreground_npa_beta.png b/resources/images/android/xxxhdpi/foreground_npa_beta.png new file mode 100644 index 0000000..9437e83 Binary files /dev/null and b/resources/images/android/xxxhdpi/foreground_npa_beta.png differ diff --git a/resources/images/android/xxxhdpi/foreground_npa_preview.png b/resources/images/android/xxxhdpi/foreground_npa_preview.png new file mode 100644 index 0000000..c9856f4 Binary files /dev/null and b/resources/images/android/xxxhdpi/foreground_npa_preview.png differ diff --git a/resources/images/android/xxxhdpi/npa.png b/resources/images/android/xxxhdpi/npa.png index fe60df1..697851e 100644 Binary files a/resources/images/android/xxxhdpi/npa.png and b/resources/images/android/xxxhdpi/npa.png differ diff --git a/resources/images/android/xxxhdpi/npa_beta.png b/resources/images/android/xxxhdpi/npa_beta.png index 49a1fa4..85ab3ca 100644 Binary files a/resources/images/android/xxxhdpi/npa_beta.png and b/resources/images/android/xxxhdpi/npa_beta.png differ diff --git a/resources/images/android/xxxhdpi/npa_preview.png b/resources/images/android/xxxhdpi/npa_preview.png index 26251a9..f1bca97 100644 Binary files a/resources/images/android/xxxhdpi/npa_preview.png and b/resources/images/android/xxxhdpi/npa_preview.png differ diff --git a/resources/images/arrowLeft.svg b/resources/images/arrowLeft.svg new file mode 100644 index 0000000..1267a28 --- /dev/null +++ b/resources/images/arrowLeft.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/resources/images/arrowRight.svg b/resources/images/arrowRight.svg index 0f3d647..1909053 100644 --- a/resources/images/arrowRight.svg +++ b/resources/images/arrowRight.svg @@ -1,10 +1,5 @@ - - - - - - - + + + + \ No newline at end of file diff --git a/resources/images/desktop/background.png b/resources/images/desktop/background.png deleted file mode 100644 index d3ceb7f..0000000 Binary files a/resources/images/desktop/background.png and /dev/null differ diff --git a/resources/images/desktop/bell_green.svg b/resources/images/desktop/bell_green.svg new file mode 100644 index 0000000..8d1506d --- /dev/null +++ b/resources/images/desktop/bell_green.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/desktop/bell_red.svg b/resources/images/desktop/bell_red.svg new file mode 100644 index 0000000..e7d1194 --- /dev/null +++ b/resources/images/desktop/bell_red.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/desktop/bell_white.svg b/resources/images/desktop/bell_white.svg new file mode 100644 index 0000000..246cc4b --- /dev/null +++ b/resources/images/desktop/bell_white.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/desktop/id_card.png b/resources/images/desktop/id_card.png new file mode 100644 index 0000000..448ed85 Binary files /dev/null and b/resources/images/desktop/id_card.png differ diff --git a/resources/images/desktop/info_application_log.svg b/resources/images/desktop/info_application_log.svg new file mode 100644 index 0000000..fe6949c --- /dev/null +++ b/resources/images/desktop/info_application_log.svg @@ -0,0 +1,5 @@ + + + + diff --git a/resources/images/desktop/info_diagnosis.svg b/resources/images/desktop/info_diagnosis.svg new file mode 100644 index 0000000..5c65230 --- /dev/null +++ b/resources/images/desktop/info_diagnosis.svg @@ -0,0 +1,5 @@ + + + + diff --git a/resources/images/desktop/info_license.svg b/resources/images/desktop/info_license.svg new file mode 100644 index 0000000..6e12361 --- /dev/null +++ b/resources/images/desktop/info_license.svg @@ -0,0 +1,5 @@ + + + + diff --git a/resources/images/desktop/info_manual.svg b/resources/images/desktop/info_manual.svg new file mode 100644 index 0000000..9f57a2f --- /dev/null +++ b/resources/images/desktop/info_manual.svg @@ -0,0 +1,5 @@ + + + + diff --git a/resources/images/desktop/info_questions.svg b/resources/images/desktop/info_questions.svg new file mode 100644 index 0000000..dc52e41 --- /dev/null +++ b/resources/images/desktop/info_questions.svg @@ -0,0 +1,5 @@ + + + + diff --git a/resources/images/desktop/info_rate_application.svg b/resources/images/desktop/info_rate_application.svg new file mode 100644 index 0000000..05f9b8f --- /dev/null +++ b/resources/images/desktop/info_rate_application.svg @@ -0,0 +1,5 @@ + + + + diff --git a/resources/images/desktop/info_report_error.svg b/resources/images/desktop/info_report_error.svg new file mode 100644 index 0000000..620a067 --- /dev/null +++ b/resources/images/desktop/info_report_error.svg @@ -0,0 +1,5 @@ + + + + diff --git a/resources/images/desktop/info_setup.svg b/resources/images/desktop/info_setup.svg new file mode 100644 index 0000000..bdc623c --- /dev/null +++ b/resources/images/desktop/info_setup.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/desktop/info_version.svg b/resources/images/desktop/info_version.svg new file mode 100644 index 0000000..27ec465 --- /dev/null +++ b/resources/images/desktop/info_version.svg @@ -0,0 +1,5 @@ + + + + diff --git a/resources/images/desktop/main_info.svg b/resources/images/desktop/main_info.svg new file mode 100644 index 0000000..fa6da26 --- /dev/null +++ b/resources/images/desktop/main_info.svg @@ -0,0 +1,6 @@ + + + + diff --git a/resources/images/desktop/pin-letter-page1.png b/resources/images/desktop/pin-letter-page1.png new file mode 100644 index 0000000..b634c51 Binary files /dev/null and b/resources/images/desktop/pin-letter-page1.png differ diff --git a/resources/images/desktop/pin-letter-page2.png b/resources/images/desktop/pin-letter-page2.png new file mode 100644 index 0000000..78ae175 Binary files /dev/null and b/resources/images/desktop/pin-letter-page2.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png index 20e8cba..5168d12 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon1024.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png index 2509bd6..0d1f004 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png index 23f6f0d..26882a7 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png index 9145e70..a62ff87 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon20@3x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png index 863ded6..77a048a 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png index bc99dc2..0b67385 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon60@3x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png index 9c824ad..5c30d26 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png index 505dd27..dec0385 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon76@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png index 15eb314..50f84a5 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png index a31b807..76b5ff0 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png index feea787..26882a7 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png index bad568f..22a7bbc 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png index 863ded6..77a048a 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png index a72a7e8..d14b925 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png index 72ea975..9edc4b4 100644 Binary files a/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png and b/resources/images/iOS/appIcons/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/Contents.json b/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..80d250a --- /dev/null +++ b/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "launchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "launchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "launchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage.png b/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage.png new file mode 100644 index 0000000..d07feca Binary files /dev/null and b/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@2x.png b/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@2x.png new file mode 100644 index 0000000..6430d2c Binary files /dev/null and b/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@2x.png differ diff --git a/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@3x.png b/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@3x.png new file mode 100644 index 0000000..fa3982d Binary files /dev/null and b/resources/images/iOS/appIcons/Images.xcassets/LaunchImage.imageset/launchImage@3x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png index 683b4c2..06fdd19 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon1024.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png index 6f016d6..a47884f 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png index 5e08203..ec81e49 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png index 8ca64e6..7b68e59 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon20@3x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png index 4765459..401bb73 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png index cb4dc95..46b220c 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon60@3x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png index d1e6f96..a746ad6 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png index 0d3c796..2c0bea2 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon76@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png index 759f0f2..fe52324 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/icon83.5@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png index db44873..4c6067f 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png index 5e08203..ec81e49 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png index 8171cb0..c36f6d6 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png index 4765459..401bb73 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall40@3x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png index 9066b7f..9988c13 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png index 23c131b..e6c6d18 100644 Binary files a/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png and b/resources/images/iOS/appIcons/beta/Images.xcassets/AppIcon.appiconset/iconSmall@3x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/Contents.json b/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..80d250a --- /dev/null +++ b/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "launchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "launchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "launchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/launchImage.png b/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/launchImage.png new file mode 100755 index 0000000..8332ab1 Binary files /dev/null and b/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/launchImage.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/launchImage@2x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/launchImage@2x.png new file mode 100755 index 0000000..0cabe41 Binary files /dev/null and b/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/launchImage@2x.png differ diff --git a/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/launchImage@3x.png b/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/launchImage@3x.png new file mode 100755 index 0000000..280579c Binary files /dev/null and b/resources/images/iOS/appIcons/beta/Images.xcassets/LaunchImage.imageset/launchImage@3x.png differ diff --git a/resources/images/iOS/appIcons/npa.svg b/resources/images/iOS/appIcons/npa.svg new file mode 100644 index 0000000..f8caf3a --- /dev/null +++ b/resources/images/iOS/appIcons/npa.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/iOS/appIcons/npa_beta.svg b/resources/images/iOS/appIcons/npa_beta.svg new file mode 100644 index 0000000..4df587b --- /dev/null +++ b/resources/images/iOS/appIcons/npa_beta.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/iOS/launchImages/Default-568h@2x.png b/resources/images/iOS/launchImages/Default-568h@2x.png deleted file mode 100644 index 9ea220a..0000000 Binary files a/resources/images/iOS/launchImages/Default-568h@2x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage1024@2x.png b/resources/images/iOS/launchImages/launchImage1024@2x.png deleted file mode 100644 index 49b614d..0000000 Binary files a/resources/images/iOS/launchImages/launchImage1024@2x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage1024@3x.png b/resources/images/iOS/launchImages/launchImage1024@3x.png deleted file mode 100644 index 49adb83..0000000 Binary files a/resources/images/iOS/launchImages/launchImage1024@3x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage1112@2x.png b/resources/images/iOS/launchImages/launchImage1112@2x.png deleted file mode 100644 index 97f41a2..0000000 Binary files a/resources/images/iOS/launchImages/launchImage1112@2x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage1112@3x.png b/resources/images/iOS/launchImages/launchImage1112@3x.png deleted file mode 100644 index 592076e..0000000 Binary files a/resources/images/iOS/launchImages/launchImage1112@3x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage1366@2x.png b/resources/images/iOS/launchImages/launchImage1366@2x.png deleted file mode 100644 index 82f716c..0000000 Binary files a/resources/images/iOS/launchImages/launchImage1366@2x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage1366@3x.png b/resources/images/iOS/launchImages/launchImage1366@3x.png deleted file mode 100644 index d3b039e..0000000 Binary files a/resources/images/iOS/launchImages/launchImage1366@3x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage568@2x.png b/resources/images/iOS/launchImages/launchImage568@2x.png deleted file mode 100644 index 3e96d42..0000000 Binary files a/resources/images/iOS/launchImages/launchImage568@2x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage568@3x.png b/resources/images/iOS/launchImages/launchImage568@3x.png deleted file mode 100644 index 8a91010..0000000 Binary files a/resources/images/iOS/launchImages/launchImage568@3x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage667@2x.png b/resources/images/iOS/launchImages/launchImage667@2x.png deleted file mode 100644 index b8bb764..0000000 Binary files a/resources/images/iOS/launchImages/launchImage667@2x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage667@3x.png b/resources/images/iOS/launchImages/launchImage667@3x.png deleted file mode 100644 index 68ae22f..0000000 Binary files a/resources/images/iOS/launchImages/launchImage667@3x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage736@2x.png b/resources/images/iOS/launchImages/launchImage736@2x.png deleted file mode 100644 index 4fa2ffa..0000000 Binary files a/resources/images/iOS/launchImages/launchImage736@2x.png and /dev/null differ diff --git a/resources/images/iOS/launchImages/launchImage736@3x.png b/resources/images/iOS/launchImages/launchImage736@3x.png deleted file mode 100644 index 6c6dfbd..0000000 Binary files a/resources/images/iOS/launchImages/launchImage736@3x.png and /dev/null differ diff --git a/resources/images/iOS/more/icon_mehr_npa.svg b/resources/images/iOS/more/icon_mehr_npa.svg new file mode 100644 index 0000000..fb1935b --- /dev/null +++ b/resources/images/iOS/more/icon_mehr_npa.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + diff --git a/resources/images/iOS/more/icon_mehr_remotereader.svg b/resources/images/iOS/more/icon_mehr_remotereader.svg deleted file mode 100644 index 31955bc..0000000 --- a/resources/images/iOS/more/icon_mehr_remotereader.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/resources/images/iOS/more/icon_mehr_report.svg b/resources/images/iOS/more/icon_mehr_report.svg new file mode 100644 index 0000000..d98ef32 --- /dev/null +++ b/resources/images/iOS/more/icon_mehr_report.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/iOS/more/icon_mehr_verlauf.svg b/resources/images/iOS/more/icon_mehr_verlauf.svg new file mode 100644 index 0000000..7072ed0 --- /dev/null +++ b/resources/images/iOS/more/icon_mehr_verlauf.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/resources/images/iOS/tabBar/Anbieter-off.png b/resources/images/iOS/tabBar/Anbieter-off.png deleted file mode 100644 index 7e11bca..0000000 Binary files a/resources/images/iOS/tabBar/Anbieter-off.png and /dev/null differ diff --git a/resources/images/iOS/tabBar/Anbieter-on.png b/resources/images/iOS/tabBar/Anbieter-on.png deleted file mode 100644 index 7b98ecd..0000000 Binary files a/resources/images/iOS/tabBar/Anbieter-on.png and /dev/null differ diff --git a/resources/images/iOS/tabBar/Ausweisen-off.png b/resources/images/iOS/tabBar/Ausweisen-off.png deleted file mode 100644 index 3078c84..0000000 Binary files a/resources/images/iOS/tabBar/Ausweisen-off.png and /dev/null differ diff --git a/resources/images/iOS/tabBar/Ausweisen-on.png b/resources/images/iOS/tabBar/Ausweisen-on.png deleted file mode 100644 index 7b2e30a..0000000 Binary files a/resources/images/iOS/tabBar/Ausweisen-on.png and /dev/null differ diff --git a/resources/images/iOS/tabBar/More-off.svg b/resources/images/iOS/tabBar/More-off.svg deleted file mode 100644 index c38a563..0000000 --- a/resources/images/iOS/tabBar/More-off.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/resources/images/iOS/tabBar/More-on.svg b/resources/images/iOS/tabBar/More-on.svg deleted file mode 100644 index 1ed93a6..0000000 --- a/resources/images/iOS/tabBar/More-on.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/resources/images/iOS/tabBar/Pin-off.png b/resources/images/iOS/tabBar/Pin-off.png deleted file mode 100644 index 8b88a35..0000000 Binary files a/resources/images/iOS/tabBar/Pin-off.png and /dev/null differ diff --git a/resources/images/iOS/tabBar/Pin-on.png b/resources/images/iOS/tabBar/Pin-on.png deleted file mode 100644 index 48d4e23..0000000 Binary files a/resources/images/iOS/tabBar/Pin-on.png and /dev/null differ diff --git a/resources/images/iOS/tabBar/Verlauf-off.png b/resources/images/iOS/tabBar/Verlauf-off.png deleted file mode 100644 index 7f02c3c..0000000 Binary files a/resources/images/iOS/tabBar/Verlauf-off.png and /dev/null differ diff --git a/resources/images/iOS/tabBar/Verlauf-on.png b/resources/images/iOS/tabBar/Verlauf-on.png deleted file mode 100644 index 5b91ecb..0000000 Binary files a/resources/images/iOS/tabBar/Verlauf-on.png and /dev/null differ diff --git a/resources/images/iOS/tabBar/anbieter.svg b/resources/images/iOS/tabBar/anbieter.svg new file mode 100644 index 0000000..11b0a22 --- /dev/null +++ b/resources/images/iOS/tabBar/anbieter.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/resources/images/iOS/tabBar/ausweisen.svg b/resources/images/iOS/tabBar/ausweisen.svg new file mode 100644 index 0000000..5cd91c6 --- /dev/null +++ b/resources/images/iOS/tabBar/ausweisen.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/resources/images/iOS/tabBar/more.svg b/resources/images/iOS/tabBar/more.svg new file mode 100644 index 0000000..624cac4 --- /dev/null +++ b/resources/images/iOS/tabBar/more.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/iOS/tabBar/pin.svg b/resources/images/iOS/tabBar/pin.svg new file mode 100644 index 0000000..8528768 --- /dev/null +++ b/resources/images/iOS/tabBar/pin.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/iOS/tabBar/remoteleser.svg b/resources/images/iOS/tabBar/remoteleser.svg new file mode 100644 index 0000000..c1743b3 --- /dev/null +++ b/resources/images/iOS/tabBar/remoteleser.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/icon_remote_0.svg b/resources/images/icon_remote_0.svg new file mode 100755 index 0000000..2aba743 --- /dev/null +++ b/resources/images/icon_remote_0.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/icon_remote_100.svg b/resources/images/icon_remote_100.svg new file mode 100755 index 0000000..f6dc99c --- /dev/null +++ b/resources/images/icon_remote_100.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/icon_remote_25.svg b/resources/images/icon_remote_25.svg new file mode 100755 index 0000000..687d654 --- /dev/null +++ b/resources/images/icon_remote_25.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/icon_remote_50.svg b/resources/images/icon_remote_50.svg new file mode 100755 index 0000000..e5e81d3 --- /dev/null +++ b/resources/images/icon_remote_50.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/icon_remote_75.svg b/resources/images/icon_remote_75.svg new file mode 100755 index 0000000..8ce2013 --- /dev/null +++ b/resources/images/icon_remote_75.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/icon_remote_inactive.svg b/resources/images/icon_remote_inactive.svg new file mode 100755 index 0000000..fc08bd4 --- /dev/null +++ b/resources/images/icon_remote_inactive.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/icon_save.svg b/resources/images/icon_save.svg new file mode 100644 index 0000000..c2ff3fd --- /dev/null +++ b/resources/images/icon_save.svg @@ -0,0 +1 @@ + diff --git a/resources/images/info.svg b/resources/images/info.svg new file mode 100644 index 0000000..da2a609 --- /dev/null +++ b/resources/images/info.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/info_filled.svg b/resources/images/info_filled.svg new file mode 100644 index 0000000..f97857b --- /dev/null +++ b/resources/images/info_filled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/npa_docs.png b/resources/images/npa_docs.png new file mode 100644 index 0000000..8b5c911 Binary files /dev/null and b/resources/images/npa_docs.png differ diff --git a/resources/images/provider/+tablet/adresse.png b/resources/images/provider/+tablet/adresse.png deleted file mode 100644 index bb2e9ae..0000000 Binary files a/resources/images/provider/+tablet/adresse.png and /dev/null differ diff --git a/resources/images/provider/+tablet/mail.png b/resources/images/provider/+tablet/mail.png deleted file mode 100644 index 5fa9ce8..0000000 Binary files a/resources/images/provider/+tablet/mail.png and /dev/null differ diff --git a/resources/images/provider/+tablet/telefon.png b/resources/images/provider/+tablet/telefon.png deleted file mode 100644 index ae67909..0000000 Binary files a/resources/images/provider/+tablet/telefon.png and /dev/null differ diff --git a/resources/images/provider/+tablet/url.png b/resources/images/provider/+tablet/url.png deleted file mode 100644 index 8a0871e..0000000 Binary files a/resources/images/provider/+tablet/url.png and /dev/null differ diff --git a/resources/images/provider/adresse.png b/resources/images/provider/adresse.png deleted file mode 100644 index 1c899e2..0000000 Binary files a/resources/images/provider/adresse.png and /dev/null differ diff --git a/resources/images/provider/adresse.svg b/resources/images/provider/adresse.svg new file mode 100644 index 0000000..d0841d7 --- /dev/null +++ b/resources/images/provider/adresse.svg @@ -0,0 +1 @@ + diff --git a/resources/images/provider/mail.png b/resources/images/provider/mail.png deleted file mode 100644 index de6ac38..0000000 Binary files a/resources/images/provider/mail.png and /dev/null differ diff --git a/resources/images/provider/mail.svg b/resources/images/provider/mail.svg new file mode 100644 index 0000000..dc0a27b --- /dev/null +++ b/resources/images/provider/mail.svg @@ -0,0 +1 @@ + diff --git a/resources/images/provider/telefon.png b/resources/images/provider/telefon.png deleted file mode 100644 index 9275ec5..0000000 Binary files a/resources/images/provider/telefon.png and /dev/null differ diff --git a/resources/images/provider/telefon.svg b/resources/images/provider/telefon.svg new file mode 100644 index 0000000..57ebd27 --- /dev/null +++ b/resources/images/provider/telefon.svg @@ -0,0 +1 @@ + diff --git a/resources/images/provider/url.png b/resources/images/provider/url.png deleted file mode 100644 index bd46864..0000000 Binary files a/resources/images/provider/url.png and /dev/null differ diff --git a/resources/images/provider/url.svg b/resources/images/provider/url.svg new file mode 100644 index 0000000..76759bf --- /dev/null +++ b/resources/images/provider/url.svg @@ -0,0 +1 @@ + diff --git a/resources/images/reader/default_card_position.png b/resources/images/reader/default_card_position.png new file mode 100644 index 0000000..d68790f Binary files /dev/null and b/resources/images/reader/default_card_position.png differ diff --git a/resources/images/reader/src/img_DefaultReader_card_position.svg b/resources/images/reader/src/img_DefaultReader_card_position.svg new file mode 100644 index 0000000..fe8e0ab --- /dev/null +++ b/resources/images/reader/src/img_DefaultReader_card_position.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/search_cancel.svg b/resources/images/search_cancel.svg new file mode 100644 index 0000000..5b6c04c --- /dev/null +++ b/resources/images/search_cancel.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/search_icon.svg b/resources/images/search_icon.svg new file mode 100644 index 0000000..20925a7 --- /dev/null +++ b/resources/images/search_icon.svg @@ -0,0 +1,6 @@ + + + + diff --git a/resources/images/triangle.svg b/resources/images/triangle.svg new file mode 100644 index 0000000..93d81f5 --- /dev/null +++ b/resources/images/triangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/tutorial/button_de.png b/resources/images/tutorial/button_de.png index 1ae97dc..944596a 100644 Binary files a/resources/images/tutorial/button_de.png and b/resources/images/tutorial/button_de.png differ diff --git a/resources/images/tutorial/button_en.png b/resources/images/tutorial/button_en.png index cd48a74..d37c510 100644 Binary files a/resources/images/tutorial/button_en.png and b/resources/images/tutorial/button_en.png differ diff --git a/resources/images/tutorial/generated/reader_sac_menu_de.svg b/resources/images/tutorial/generated/reader_sac_menu_android_de.svg similarity index 87% rename from resources/images/tutorial/generated/reader_sac_menu_de.svg rename to resources/images/tutorial/generated/reader_sac_menu_android_de.svg index 7700d27..054c195 100644 --- a/resources/images/tutorial/generated/reader_sac_menu_de.svg +++ b/resources/images/tutorial/generated/reader_sac_menu_android_de.svg @@ -1,6 +1,6 @@ - + diff --git a/resources/images/tutorial/generated/reader_sac_menu_en.svg b/resources/images/tutorial/generated/reader_sac_menu_android_en.svg similarity index 87% rename from resources/images/tutorial/generated/reader_sac_menu_en.svg rename to resources/images/tutorial/generated/reader_sac_menu_android_en.svg index 0c4f728..8796870 100644 --- a/resources/images/tutorial/generated/reader_sac_menu_en.svg +++ b/resources/images/tutorial/generated/reader_sac_menu_android_en.svg @@ -1,6 +1,6 @@ - + diff --git a/resources/images/tutorial/generated/reader_sac_menu_ios_de.svg b/resources/images/tutorial/generated/reader_sac_menu_ios_de.svg new file mode 100644 index 0000000..2d7c6a1 --- /dev/null +++ b/resources/images/tutorial/generated/reader_sac_menu_ios_de.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/reader_sac_menu_ios_en.svg b/resources/images/tutorial/generated/reader_sac_menu_ios_en.svg new file mode 100644 index 0000000..2c0d16f --- /dev/null +++ b/resources/images/tutorial/generated/reader_sac_menu_ios_en.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/where_identify_now_de.svg b/resources/images/tutorial/generated/where_identify_now_de.svg index ba51b50..12a7bb0 100644 --- a/resources/images/tutorial/generated/where_identify_now_de.svg +++ b/resources/images/tutorial/generated/where_identify_now_de.svg @@ -1,6 +1,6 @@ - + diff --git a/resources/images/tutorial/generated/where_identify_now_en.svg b/resources/images/tutorial/generated/where_identify_now_en.svg index 0f308f3..27fab48 100644 --- a/resources/images/tutorial/generated/where_identify_now_en.svg +++ b/resources/images/tutorial/generated/where_identify_now_en.svg @@ -1,6 +1,6 @@ - + diff --git a/resources/images/tutorial/generated/where_providerlist_screenshot_android_de.svg b/resources/images/tutorial/generated/where_providerlist_screenshot_android_de.svg new file mode 100644 index 0000000..eda02b3 --- /dev/null +++ b/resources/images/tutorial/generated/where_providerlist_screenshot_android_de.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/where_providerlist_screenshot_android_en.svg b/resources/images/tutorial/generated/where_providerlist_screenshot_android_en.svg new file mode 100644 index 0000000..285aa78 --- /dev/null +++ b/resources/images/tutorial/generated/where_providerlist_screenshot_android_en.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/resources/images/tutorial/generated/where_providerlist_screenshot_de.svg b/resources/images/tutorial/generated/where_providerlist_screenshot_de.svg index 8977815..eda02b3 100644 --- a/resources/images/tutorial/generated/where_providerlist_screenshot_de.svg +++ b/resources/images/tutorial/generated/where_providerlist_screenshot_de.svg @@ -3,8 +3,8 @@ - - + + diff --git a/resources/images/tutorial/generated/where_providerlist_screenshot_en.svg b/resources/images/tutorial/generated/where_providerlist_screenshot_en.svg index 420d9b8..285aa78 100644 --- a/resources/images/tutorial/generated/where_providerlist_screenshot_en.svg +++ b/resources/images/tutorial/generated/where_providerlist_screenshot_en.svg @@ -3,8 +3,8 @@ - - + + diff --git a/resources/images/tutorial/phone_screen_de.jpg b/resources/images/tutorial/phone_screen_de.jpg index 2ae2b87..9e663bf 100644 Binary files a/resources/images/tutorial/phone_screen_de.jpg and b/resources/images/tutorial/phone_screen_de.jpg differ diff --git a/resources/images/tutorial/phone_screen_en.jpg b/resources/images/tutorial/phone_screen_en.jpg index 0af3ba0..3497652 100644 Binary files a/resources/images/tutorial/phone_screen_en.jpg and b/resources/images/tutorial/phone_screen_en.jpg differ diff --git a/resources/images/tutorial/screenshot_cert_android_de.png b/resources/images/tutorial/screenshot_cert_android_de.png new file mode 100644 index 0000000..b5a66a5 Binary files /dev/null and b/resources/images/tutorial/screenshot_cert_android_de.png differ diff --git a/resources/images/tutorial/screenshot_cert_android_en.png b/resources/images/tutorial/screenshot_cert_android_en.png new file mode 100644 index 0000000..fbe0358 Binary files /dev/null and b/resources/images/tutorial/screenshot_cert_android_en.png differ diff --git a/resources/images/tutorial/screenshot_cert_de.png b/resources/images/tutorial/screenshot_cert_de.png deleted file mode 100644 index 9215ebd..0000000 Binary files a/resources/images/tutorial/screenshot_cert_de.png and /dev/null differ diff --git a/resources/images/tutorial/screenshot_cert_en.png b/resources/images/tutorial/screenshot_cert_en.png deleted file mode 100644 index 06d6687..0000000 Binary files a/resources/images/tutorial/screenshot_cert_en.png and /dev/null differ diff --git a/resources/images/tutorial/screenshot_cert_ios_de.png b/resources/images/tutorial/screenshot_cert_ios_de.png new file mode 100644 index 0000000..5ac642d Binary files /dev/null and b/resources/images/tutorial/screenshot_cert_ios_de.png differ diff --git a/resources/images/tutorial/screenshot_cert_ios_en.png b/resources/images/tutorial/screenshot_cert_ios_en.png new file mode 100644 index 0000000..597ee9d Binary files /dev/null and b/resources/images/tutorial/screenshot_cert_ios_en.png differ diff --git a/resources/images/tutorial/screenshot_choose_reader_de.png b/resources/images/tutorial/screenshot_choose_reader_android_de.png similarity index 100% rename from resources/images/tutorial/screenshot_choose_reader_de.png rename to resources/images/tutorial/screenshot_choose_reader_android_de.png diff --git a/resources/images/tutorial/screenshot_choose_reader_en.png b/resources/images/tutorial/screenshot_choose_reader_android_en.png similarity index 100% rename from resources/images/tutorial/screenshot_choose_reader_en.png rename to resources/images/tutorial/screenshot_choose_reader_android_en.png diff --git a/resources/images/tutorial/screenshot_choose_reader_ios_de.png b/resources/images/tutorial/screenshot_choose_reader_ios_de.png new file mode 100644 index 0000000..8ef7580 Binary files /dev/null and b/resources/images/tutorial/screenshot_choose_reader_ios_de.png differ diff --git a/resources/images/tutorial/screenshot_choose_reader_ios_en.png b/resources/images/tutorial/screenshot_choose_reader_ios_en.png new file mode 100644 index 0000000..0127f81 Binary files /dev/null and b/resources/images/tutorial/screenshot_choose_reader_ios_en.png differ diff --git a/resources/images/tutorial/screenshot_menu_providerlist_de.png b/resources/images/tutorial/screenshot_menu_providerlist_android_de.png similarity index 100% rename from resources/images/tutorial/screenshot_menu_providerlist_de.png rename to resources/images/tutorial/screenshot_menu_providerlist_android_de.png diff --git a/resources/images/tutorial/screenshot_menu_providerlist_en.png b/resources/images/tutorial/screenshot_menu_providerlist_android_en.png similarity index 100% rename from resources/images/tutorial/screenshot_menu_providerlist_en.png rename to resources/images/tutorial/screenshot_menu_providerlist_android_en.png diff --git a/resources/images/tutorial/screenshot_pin_management_menu_de.png b/resources/images/tutorial/screenshot_pin_management_menu_android_de.png similarity index 100% rename from resources/images/tutorial/screenshot_pin_management_menu_de.png rename to resources/images/tutorial/screenshot_pin_management_menu_android_de.png diff --git a/resources/images/tutorial/screenshot_pin_management_menu_en.png b/resources/images/tutorial/screenshot_pin_management_menu_android_en.png similarity index 100% rename from resources/images/tutorial/screenshot_pin_management_menu_en.png rename to resources/images/tutorial/screenshot_pin_management_menu_android_en.png diff --git a/resources/images/tutorial/screenshot_pin_management_menu_ios_de.png b/resources/images/tutorial/screenshot_pin_management_menu_ios_de.png new file mode 100755 index 0000000..16cf46e Binary files /dev/null and b/resources/images/tutorial/screenshot_pin_management_menu_ios_de.png differ diff --git a/resources/images/tutorial/screenshot_pin_management_menu_ios_en.png b/resources/images/tutorial/screenshot_pin_management_menu_ios_en.png new file mode 100755 index 0000000..b78ef3d Binary files /dev/null and b/resources/images/tutorial/screenshot_pin_management_menu_ios_en.png differ diff --git a/resources/images/tutorial/screenshot_providerlist_de.png b/resources/images/tutorial/screenshot_providerlist_android_de.png similarity index 100% rename from resources/images/tutorial/screenshot_providerlist_de.png rename to resources/images/tutorial/screenshot_providerlist_android_de.png diff --git a/resources/images/tutorial/screenshot_providerlist_en.png b/resources/images/tutorial/screenshot_providerlist_android_en.png similarity index 100% rename from resources/images/tutorial/screenshot_providerlist_en.png rename to resources/images/tutorial/screenshot_providerlist_android_en.png diff --git a/resources/images/tutorial/screenshot_providerlist_ios_de.png b/resources/images/tutorial/screenshot_providerlist_ios_de.png new file mode 100755 index 0000000..84ec73f Binary files /dev/null and b/resources/images/tutorial/screenshot_providerlist_ios_de.png differ diff --git a/resources/images/tutorial/screenshot_providerlist_ios_en.png b/resources/images/tutorial/screenshot_providerlist_ios_en.png new file mode 100755 index 0000000..6874a4b Binary files /dev/null and b/resources/images/tutorial/screenshot_providerlist_ios_en.png differ diff --git a/resources/images/tutorial/screenshot_remoteservice_ios_de.png b/resources/images/tutorial/screenshot_remoteservice_ios_de.png new file mode 100755 index 0000000..e587797 Binary files /dev/null and b/resources/images/tutorial/screenshot_remoteservice_ios_de.png differ diff --git a/resources/images/tutorial/screenshot_remoteservice_ios_en.png b/resources/images/tutorial/screenshot_remoteservice_ios_en.png new file mode 100755 index 0000000..b61ab99 Binary files /dev/null and b/resources/images/tutorial/screenshot_remoteservice_ios_en.png differ diff --git a/resources/images/tutorial/screenshot_sac_menu_de.png b/resources/images/tutorial/screenshot_sac_menu_android_de.png similarity index 100% rename from resources/images/tutorial/screenshot_sac_menu_de.png rename to resources/images/tutorial/screenshot_sac_menu_android_de.png diff --git a/resources/images/tutorial/screenshot_sac_menu_en.png b/resources/images/tutorial/screenshot_sac_menu_android_en.png similarity index 100% rename from resources/images/tutorial/screenshot_sac_menu_en.png rename to resources/images/tutorial/screenshot_sac_menu_android_en.png diff --git a/resources/images/tutorial/src/reader_sac_menu_en.svg b/resources/images/tutorial/src/reader_sac_menu_android_de.svg similarity index 86% rename from resources/images/tutorial/src/reader_sac_menu_en.svg rename to resources/images/tutorial/src/reader_sac_menu_android_de.svg index 40a4fb8..3b48834 100644 --- a/resources/images/tutorial/src/reader_sac_menu_en.svg +++ b/resources/images/tutorial/src/reader_sac_menu_android_de.svg @@ -1,6 +1,6 @@ - + diff --git a/resources/images/tutorial/src/reader_sac_menu_de.svg b/resources/images/tutorial/src/reader_sac_menu_android_en.svg similarity index 86% rename from resources/images/tutorial/src/reader_sac_menu_de.svg rename to resources/images/tutorial/src/reader_sac_menu_android_en.svg index e5e262a..8d39c4a 100644 --- a/resources/images/tutorial/src/reader_sac_menu_de.svg +++ b/resources/images/tutorial/src/reader_sac_menu_android_en.svg @@ -1,6 +1,6 @@ - + diff --git a/resources/images/tutorial/src/reader_sac_menu_ios_de.svg b/resources/images/tutorial/src/reader_sac_menu_ios_de.svg new file mode 100644 index 0000000..6f4993d --- /dev/null +++ b/resources/images/tutorial/src/reader_sac_menu_ios_de.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/src/reader_sac_menu_ios_en.svg b/resources/images/tutorial/src/reader_sac_menu_ios_en.svg new file mode 100644 index 0000000..cd92b05 --- /dev/null +++ b/resources/images/tutorial/src/reader_sac_menu_ios_en.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/images/tutorial/src/where_identify_now_de.svg b/resources/images/tutorial/src/where_identify_now_de.svg index a8faa61..3db85d9 100644 --- a/resources/images/tutorial/src/where_identify_now_de.svg +++ b/resources/images/tutorial/src/where_identify_now_de.svg @@ -1,6 +1,6 @@ - + diff --git a/resources/images/tutorial/src/where_identify_now_en.svg b/resources/images/tutorial/src/where_identify_now_en.svg index 4634f36..f6121f6 100644 --- a/resources/images/tutorial/src/where_identify_now_en.svg +++ b/resources/images/tutorial/src/where_identify_now_en.svg @@ -1,6 +1,6 @@ - + diff --git a/resources/images/tutorial/src/where_providerlist_screenshot_de.svg b/resources/images/tutorial/src/where_providerlist_screenshot_android_de.svg similarity index 80% rename from resources/images/tutorial/src/where_providerlist_screenshot_de.svg rename to resources/images/tutorial/src/where_providerlist_screenshot_android_de.svg index 116237e..fda962b 100644 --- a/resources/images/tutorial/src/where_providerlist_screenshot_de.svg +++ b/resources/images/tutorial/src/where_providerlist_screenshot_android_de.svg @@ -3,8 +3,8 @@ - - + + diff --git a/resources/images/tutorial/src/where_providerlist_screenshot_en.svg b/resources/images/tutorial/src/where_providerlist_screenshot_android_en.svg similarity index 80% rename from resources/images/tutorial/src/where_providerlist_screenshot_en.svg rename to resources/images/tutorial/src/where_providerlist_screenshot_android_en.svg index 1ff21dc..eace54f 100644 --- a/resources/images/tutorial/src/where_providerlist_screenshot_en.svg +++ b/resources/images/tutorial/src/where_providerlist_screenshot_android_en.svg @@ -3,8 +3,8 @@ - - + + diff --git a/resources/jenkins/clang-gcov.py b/resources/jenkins/clang-gcov.py deleted file mode 100755 index 29cdf28..0000000 --- a/resources/jenkins/clang-gcov.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python - -import subprocess -import sys - -cmd = "llvm-cov gcov " + " ".join(sys.argv[1:]) -subprocess.call(cmd, shell=True) diff --git a/resources/jenkins/clang-gcov.sh b/resources/jenkins/clang-gcov.sh deleted file mode 100755 index f932bb8..0000000 --- a/resources/jenkins/clang-gcov.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -llvm-cov gcov $@ diff --git a/resources/jenkins/docker/alpine/Dockerfile b/resources/jenkins/docker/alpine/Dockerfile index 6e05d84..79be345 100644 --- a/resources/jenkins/docker/alpine/Dockerfile +++ b/resources/jenkins/docker/alpine/Dockerfile @@ -1,7 +1,7 @@ FROM scratch MAINTAINER Governikus KG -ARG version="3.9.0" +ARG version="3.10.0" ARG arch="x86_64" ADD alpine-minirootfs-$version-$arch.tar.gz / diff --git a/resources/jenkins/docker/android/Dockerfile b/resources/jenkins/docker/android/Dockerfile deleted file mode 100644 index 495a206..0000000 --- a/resources/jenkins/docker/android/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM arch:latest -MAINTAINER Governikus KG - -ENV NAME=Android LABELS=Android - -RUN echo "[multilib]" >> /etc/pacman.conf && echo 'Include = /etc/pacman.d/mirrorlist' >> /etc/pacman.conf - -RUN chown -R governikus: /var/cache/pacman/pkg/ - -ARG JENKINS_SWARM_VERSION=3.15 -ARG TINI_VERSION=0.18.0 -RUN curl -L -o /sbin/tini https://github.com/krallin/tini/releases/download/v$TINI_VERSION/tini-static-muslc-amd64 && chmod 755 /sbin/tini && \ - curl -L -o /swarm-client.jar https://repo.jenkins-ci.org/releases/org/jenkins-ci/plugins/swarm-client/$JENKINS_SWARM_VERSION/swarm-client-$JENKINS_SWARM_VERSION.jar -ADD ../swarm/swarm.sh / - -USER governikus -RUN mkdir -p /home/governikus/.ccache && mkdir -p /home/governikus/workspace && mkdir -p /home/governikus/packages -VOLUME /home/governikus/.ccache - -# key for ncurses sources -RUN gpg --receive-keys 702353E0F7E48EDB - -RUN pacaur -Sy --noconfirm cmake ccache python2-hglib apache-ant jdk8-openjdk jre8-openjdk-headless mercurial python2-hglib \ - android-ndk-10e android-sdk-25.2.5 android-sdk-build-tools android-sdk-platform-tools \ - android-platform-18 android-platform-21 android-platform-25 \ - && rm -rf /var/cache/pacman/pkg/* && rm -rf /home/governikus/.cache/pacaur - -ENTRYPOINT ["/sbin/tini", "--"] -CMD sh -l -c /swarm.sh diff --git a/resources/jenkins/docker/arch/Dockerfile b/resources/jenkins/docker/arch/Dockerfile deleted file mode 100644 index 2b38814..0000000 --- a/resources/jenkins/docker/arch/Dockerfile +++ /dev/null @@ -1,55 +0,0 @@ -FROM scratch -MAINTAINER Governikus KG - -ARG box="busybox-x86_64" -ARG version="2018.01.01" - -ADD $box /tmp/busybox -ADD archlinux-bootstrap-$version-x86_64.tar.gz / - -# Clean up rootfs -RUN ["/tmp/busybox", "rm", "-rf", \ - "/root.x86_64/etc/hosts", \ - "/root.x86_64/etc/hostname", \ - "/root.x86_64/etc/mtab", \ - "/root.x86_64/etc/resolv.conf", \ - "/root.x86_64/dev", \ - "/root.x86_64/proc", \ - "/root.x86_64/sys" \ - ] - -RUN ["/tmp/busybox", "sh", "-c", "/tmp/busybox cp -af /root.x86_64/* /"] - -RUN rm -rf /tmp/busybox /root.x86_64 - -# Init Arch -RUN pacman-key --init && pacman-key --populate archlinux - -RUN echo 'Server = http://mirrors.kernel.org/archlinux/$repo/os/$arch' > /etc/pacman.d/mirrorlist &&\ - echo 'Server = http://ftp.uni-hannover.de/archlinux/$repo/os/$arch' >> /etc/pacman.d/mirrorlist &&\ - echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen &&\ - echo 'LANG="en_US.UTF-8"' > /etc/locale.conf - -RUN pacman -Syu --noconfirm base-devel lzop -RUN locale-gen; - -RUN sed -i "s|PKGEXT='.pkg.tar.xz'|PKGEXT='.pkg.tar.lzo'|" /etc/makepkg.conf - -RUN useradd governikus -m -s /usr/bin/bash -G wheel -RUN echo '%wheel ALL=(ALL) NOPASSWD: /usr/bin/pacman' > /etc/sudoers.d/pacman - -# Helper for AUR -RUN mkdir /tmp/p &&\ - curl -L -o /tmp/p/cower.tar.gz https://aur.archlinux.org/cgit/aur.git/snapshot/cower.tar.gz &&\ - curl -L -o /tmp/p/pacaur.tar.gz https://aur.archlinux.org/cgit/aur.git/snapshot/pacaur.tar.gz - -RUN cd /tmp/p && tar xf cower.tar.gz && tar xf pacaur.tar.gz &&\ - chown -R governikus /tmp/p &&\ - su - governikus -c "source /etc/profile.d/perlbin.sh && cd /tmp/p/cower && makepkg -si --noconfirm --skippgpcheck" &&\ - su - governikus -c "source /etc/profile.d/perlbin.sh && cd /tmp/p/pacaur && makepkg -si --noconfirm --skippgpcheck" - -# pacaur requires VISUAL, EDITOR or vi -RUN pacman -S --noconfirm vi - - -RUN rm -rf /tmp/p && rm -rf /var/cache/pacman/pkg/* && rm -rf /home/governikus/.cache/pacaur diff --git a/resources/jenkins/docker/generate.py b/resources/jenkins/docker/generate.py new file mode 100755 index 0000000..de27c78 --- /dev/null +++ b/resources/jenkins/docker/generate.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import glob +import subprocess +import sys + +req_version = (3, 5) +cur_version = sys.version_info + +if cur_version < req_version: + print("python version >=3.5 required") + sys.exit(1) + +# Alpine +alpine_images = glob.glob("alpine/alpine-minirootfs-*.tar.gz") +if len(alpine_images) == 0: + print("No alpine image found in ./alpine") + sys.exit(1) + +print("Building base Alpine ...") +proc_args = ["docker", "build", "-t", "alpine:latest", "alpine"] +result = subprocess.run(proc_args).check_returncode() + +images = ["swarm", "trigger", "common", "docs", "linux"] +for image in images: + print("Building %s ..." % (image)) + proc_args = ["docker", "build", "-t", "alpine:%s" % (image), image] + subprocess.run().check_returncode(proc_args) diff --git a/resources/jenkins/docker/generate.sh b/resources/jenkins/docker/generate.sh deleted file mode 100755 index ba38049..0000000 --- a/resources/jenkins/docker/generate.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -set -e - -################### Alpine -cd alpine -ls alpine-minirootfs-*.tar.gz >/dev/null - -echo "Building base Alpine ..." -docker build -t alpine:latest . - -images=(swarm trigger common docs linux) -for i in "${images[@]}" -do - echo "Building $i ..." - cd ../$i - docker build -t alpine:$i . -done - diff --git a/resources/jenkins/docker/linux/Dockerfile b/resources/jenkins/docker/linux/Dockerfile index 98b2098..5a249aa 100644 --- a/resources/jenkins/docker/linux/Dockerfile +++ b/resources/jenkins/docker/linux/Dockerfile @@ -3,15 +3,12 @@ MAINTAINER Governikus KG ENV NAME=Linux LABELS="Linux g++ clang++" PACKAGES_DIR=/home/governikus/packages -RUN apk --no-cache add cmake make g++ clang clang-analyzer ccache cloc cppcheck pkgconf ninja pcsc-lite-dev binutils-gold \ - mesa-dev libx11-dev libxkbcommon-dev xcb-util-wm-dev xcb-util-image-dev xcb-util-keysyms-dev \ - py2-pip && \ - ln -s /usr/libexec/c++-analyzer /usr/local/bin && ln -s /usr/libexec/ccc-analyzer /usr/local/bin && \ - pip install gcovr && apk del py2-pip +RUN apk --no-cache add cmake make g++ clang clang-analyzer ccache gcovr cloc cppcheck pkgconf ninja pcsc-lite-dev binutils-gold lld \ + py-setuptools mesa-dev libx11-dev libxkbcommon-dev xcb-util-wm-dev xcb-util-image-dev xcb-util-keysyms-dev && \ + ln -s /usr/libexec/c++-analyzer /usr/local/bin && ln -s /usr/libexec/ccc-analyzer /usr/local/bin USER governikus RUN mkdir -p /home/governikus/.ccache && mkdir -p /home/governikus/workspace && mkdir -p /home/governikus/packages -VOLUME /home/governikus/.ccache ENTRYPOINT ["/sbin/tini", "--"] CMD /swarm.sh diff --git a/resources/jenkins/docker/swarm/Dockerfile b/resources/jenkins/docker/swarm/Dockerfile index 185e19c..612a21b 100644 --- a/resources/jenkins/docker/swarm/Dockerfile +++ b/resources/jenkins/docker/swarm/Dockerfile @@ -1,7 +1,7 @@ FROM alpine:latest MAINTAINER Governikus KG -ARG JENKINS_SWARM_VERSION=3.15 +ARG JENKINS_SWARM_VERSION=3.17 ENV EXECUTOR=3 LABELS= NAME= PASSWORD= RUN adduser governikus -s /bin/sh -D diff --git a/resources/jenkins/dsl/Builds/Build_Android.groovy b/resources/jenkins/dsl/Builds/Build_Android.groovy index 8a4faab..06c04ae 100644 --- a/resources/jenkins/dsl/Builds/Build_Android.groovy +++ b/resources/jenkins/dsl/Builds/Build_Android.groovy @@ -42,7 +42,7 @@ j.with androidLint('build/dist/build/outputs/lint-results-*.xml') { thresholds( - unstableTotal: [all: 14] + unstableTotal: [all: 1] ) } } @@ -95,7 +95,7 @@ j.with androidLint('build/dist/build/outputs/lint-results-*.xml') { thresholds( - unstableTotal: [all: 12] + unstableTotal: [all: 0] ) } } diff --git a/resources/jenkins/dsl/Builds/Build_Translation.groovy b/resources/jenkins/dsl/Builds/Build_Translation.groovy index 231e22f..57f718a 100644 --- a/resources/jenkins/dsl/Builds/Build_Translation.groovy +++ b/resources/jenkins/dsl/Builds/Build_Translation.groovy @@ -4,6 +4,7 @@ import static common.Constants.strip def j = new Build ( name: 'Translation', + excludePattern: '', libraries: ['Linux'], label: 'Linux', artifacts: 'source/resources/translations/*.ts' diff --git a/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy b/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy index 3df53a8..7141992 100644 --- a/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy +++ b/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy @@ -6,7 +6,7 @@ def j = new Build name: 'iOS_IPA', libraries: ['iOS'], label: 'iOS', - artifacts: 'build/*.ipa' + artifacts: 'build/*.ipa,build/*.zip' ).generate(this) @@ -19,13 +19,13 @@ j.with shell(strip('''\ cd build; cmake ../source - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_PREFIX_PATH=\${WORKSPACE}/libs/build/dist -DCMAKE_TOOLCHAIN_FILE=../source/cmake/iOS.toolchain.cmake -GXcode ''')) - shell('cd build; xcodebuild -target install -configuration Release ARCHS=arm64') - shell('cd build; xcodebuild -target ipa -configuration Release') + shell('cd build; xcodebuild -configuration MinSizeRel ARCHS=arm64') + shell('cd build; xcodebuild -target ipa -configuration MinSizeRel') } } diff --git a/resources/jenkins/dsl/Releases/Release_Appcast.groovy b/resources/jenkins/dsl/Releases/Release_Appcast.groovy index a68e750..92aa5ba 100644 --- a/resources/jenkins/dsl/Releases/Release_Appcast.groovy +++ b/resources/jenkins/dsl/Releases/Release_Appcast.groovy @@ -13,4 +13,9 @@ j.with { stringParam('changeset', 'release', 'Build given changeset (tag) as release') } + + wrappers + { + buildName('${changeset}') + } } diff --git a/resources/jenkins/dsl/Releases/Release_Docs.groovy b/resources/jenkins/dsl/Releases/Release_Docs.groovy index 3f35731..d7c9035 100644 --- a/resources/jenkins/dsl/Releases/Release_Docs.groovy +++ b/resources/jenkins/dsl/Releases/Release_Docs.groovy @@ -10,9 +10,14 @@ def j = new Release j.with { + parameters + { + booleanParam("ENABLE_DVCS", true, "Include mercurial tag instead of CMake version number into docs.") + } + steps { - shell('cd build; cmake ../source -DCMAKE_BUILD_TYPE=release -Dtools.only=true') + shell('cd build; cmake ../source -DCMAKE_BUILD_TYPE=release -Dtools.only=true -DENABLE_DVCS=\${ENABLE_DVCS}') shell('cd build; make notes') shell('cd build; make notes.latex.pdf') diff --git a/resources/jenkins/dsl/Releases/Release_iOS.groovy b/resources/jenkins/dsl/Releases/Release_iOS.groovy index 76e370e..a51dc7b 100644 --- a/resources/jenkins/dsl/Releases/Release_iOS.groovy +++ b/resources/jenkins/dsl/Releases/Release_iOS.groovy @@ -6,7 +6,7 @@ def j = new Release name: 'iOS_IPA', libraries: ['iOS'], label: 'iOS', - artifacts: 'build/*.ipa' + artifacts: 'build/*.ipa,build/*.zip' ).generate(this) @@ -26,14 +26,29 @@ j.with shell(strip('''\ cd build; cmake ../source - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_PREFIX_PATH=\${WORKSPACE}/libs/build/dist -DCMAKE_TOOLCHAIN_FILE=../source/cmake/iOS.toolchain.cmake -DUSE_DISTRIBUTION_PROFILE=\${USE_DISTRIBUTION_PROFILE} -GXcode ''')) - shell('cd build; xcodebuild -target install -configuration Release ARCHS=arm64') - shell('cd build; xcodebuild -target ipa -configuration Release') + shell('cd build; xcodebuild -configuration MinSizeRel ARCHS=arm64') + shell('cd build; xcodebuild -target ipa -configuration MinSizeRel') + + conditionalSteps + { + condition + { + booleanCondition('${USE_DISTRIBUTION_PROFILE}') + } + + steps + { + shell('cd build; xcrun altool -t ios --validate-app --verbose -u "ausweisapp@governikus.com" -p @env:PASSWORD -f *.ipa') + + shell('cd build; xcrun altool -t ios --upload-app --verbose -u "ausweisapp@governikus.com" -p @env:PASSWORD -f *.ipa') + } + } } } diff --git a/resources/jenkins/dsl/Reviews/Review_Android.groovy b/resources/jenkins/dsl/Reviews/Review_Android.groovy index 5c0fd48..96f1cc7 100644 --- a/resources/jenkins/dsl/Reviews/Review_Android.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Android.groovy @@ -4,7 +4,7 @@ import static common.Constants.strip // ----------------------------------------------------------------- APK -for(ARCH in Constants.AndroidArchAPK) +for(ARCH in Constants.AndroidArchAPKReview) { def j = new Review @@ -41,7 +41,7 @@ j.with androidLint('build/dist/build/outputs/lint-results-*.xml') { thresholds( - unstableTotal: [all: 14] + unstableTotal: [all: 1] ) } } @@ -101,7 +101,7 @@ j.with androidLint('build/dist/build/outputs/lint-results-*.xml') { thresholds( - unstableTotal: [all: 12] + unstableTotal: [all: 0] ) } } diff --git a/resources/jenkins/dsl/Reviews/Review_Libs_Android.groovy b/resources/jenkins/dsl/Reviews/Review_Libs_Android.groovy index e9fbfce..0bbc618 100644 --- a/resources/jenkins/dsl/Reviews/Review_Libs_Android.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Libs_Android.groovy @@ -17,7 +17,7 @@ j.with { shell('cd source; python resources/jenkins/import.py') - shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DCMAKE_TOOLCHAIN_FILE=../source/cmake/android.toolchain.cmake -DPACKAGES_DIR=\${PACKAGES_DIR} -DCMAKE_ANDROID_ARCH_ABI=${ARCH}") + shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DCMAKE_TOOLCHAIN_FILE=../source/cmake/android.toolchain.cmake -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DPACKAGES_DIR=\${PACKAGES_DIR} -DCMAKE_ANDROID_ARCH_ABI=${ARCH}") shell('cd build; make compress') } diff --git a/resources/jenkins/dsl/Reviews/Review_Libs_FreeBSD.groovy b/resources/jenkins/dsl/Reviews/Review_Libs_FreeBSD.groovy index b0bc3b1..505bd31 100644 --- a/resources/jenkins/dsl/Reviews/Review_Libs_FreeBSD.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Libs_FreeBSD.groovy @@ -13,7 +13,7 @@ j.with { shell('cd source; python resources/jenkins/import.py') - shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DPACKAGES_DIR=\${PACKAGES_DIR}") + shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=debug -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DPACKAGES_DIR=\${PACKAGES_DIR}") shell('cd build; make compress') } diff --git a/resources/jenkins/dsl/Reviews/Review_Libs_Linux.groovy b/resources/jenkins/dsl/Reviews/Review_Libs_Linux.groovy index f5cd9f9..c2ed644 100644 --- a/resources/jenkins/dsl/Reviews/Review_Libs_Linux.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Libs_Linux.groovy @@ -13,7 +13,7 @@ j.with { shell('cd source; python resources/jenkins/import.py') - shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DPACKAGES_DIR=\${PACKAGES_DIR}") + shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DPACKAGES_DIR=\${PACKAGES_DIR}") shell('cd build; make compress') } diff --git a/resources/jenkins/dsl/Reviews/Review_Libs_MacOS.groovy b/resources/jenkins/dsl/Reviews/Review_Libs_MacOS.groovy index ae9f453..3d163a1 100644 --- a/resources/jenkins/dsl/Reviews/Review_Libs_MacOS.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Libs_MacOS.groovy @@ -13,7 +13,7 @@ j.with { shell('cd source; python resources/jenkins/import.py') - shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DPACKAGES_DIR=\${PACKAGES_DIR}") + shell("cd build; cmake ../source/libs -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DPACKAGES_DIR=\${PACKAGES_DIR}") shell('cd build; make compress') } diff --git a/resources/jenkins/dsl/Reviews/Review_Trigger.groovy b/resources/jenkins/dsl/Reviews/Review_Trigger.groovy index 8410360..aed7a63 100644 --- a/resources/jenkins/dsl/Reviews/Review_Trigger.groovy +++ b/resources/jenkins/dsl/Reviews/Review_Trigger.groovy @@ -7,7 +7,7 @@ import static common.Constants.createReviewMessage def getJobs() { def list = ['Formatting', 'Source', 'Docs', 'MacOS_DMG', 'Win32_GNU_MSI', 'Win32_MSVC_MSI', 'iOS_IPA', 'Android_AAR'] - for(ARCH in Constants.AndroidArchAPK) + for(ARCH in Constants.AndroidArchAPKReview) { list << 'Android_APK_' + ARCH } @@ -64,7 +64,7 @@ j.with { phaseJob(getName('Android_AAR')) - for(ARCH in Constants.AndroidArchAPK) + for(ARCH in Constants.AndroidArchAPKReview) { phaseJob(getName('Android_APK_' + ARCH)) } diff --git a/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy b/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy index 192f4f2..239953a 100644 --- a/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy +++ b/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy @@ -6,7 +6,7 @@ def j = new Review name: 'iOS_IPA', libraries: ['iOS'], label: 'iOS', - artifacts: 'build/*.ipa' + artifacts: 'build/*.ipa,build/*.zip' ).generate(this) @@ -21,13 +21,13 @@ j.with shell(strip('''\ cd build; cmake -Werror=dev ../source - -DCMAKE_BUILD_TYPE=release + -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_PREFIX_PATH=\${WORKSPACE}/libs/build/dist -DCMAKE_TOOLCHAIN_FILE=../source/cmake/iOS.toolchain.cmake -GXcode ''')) - shell('cd build; xcodebuild -target install -configuration Release ARCHS=arm64') - shell('cd build; xcodebuild -target ipa -configuration Release') + shell('cd build; xcodebuild -configuration MinSizeRel ARCHS=arm64') + shell('cd build; xcodebuild -target ipa -configuration MinSizeRel') } } diff --git a/resources/jenkins/dsl/common/Constants.groovy b/resources/jenkins/dsl/common/Constants.groovy index 79effca..e1f7e0e 100644 --- a/resources/jenkins/dsl/common/Constants.groovy +++ b/resources/jenkins/dsl/common/Constants.groovy @@ -2,7 +2,8 @@ package common class Constants { - static final AndroidArchAPK = ["armeabi-v7a", "x86", "arm64-v8a"] + static final AndroidArchAPKReview = ["armeabi-v7a", "arm64-v8a"] + static final AndroidArchAPK = AndroidArchAPKReview + ["x86"] static final AndroidArchAAR = ["arm64-v8a"] static final AndroidArch = (AndroidArchAPK + AndroidArchAAR).unique() diff --git a/resources/jenkins/dsl/common/Release.groovy b/resources/jenkins/dsl/common/Release.groovy index fe599cc..c5000d5 100644 --- a/resources/jenkins/dsl/common/Release.groovy +++ b/resources/jenkins/dsl/common/Release.groovy @@ -23,6 +23,11 @@ class Release extends Build { stringParam('changeset', 'release', 'Build given changeset (tag) as release') } + + wrappers + { + buildName('${changeset}') + } } return j diff --git a/resources/jenkins/dsl/install.py b/resources/jenkins/dsl/install.py index 9905264..9eb2067 100755 --- a/resources/jenkins/dsl/install.py +++ b/resources/jenkins/dsl/install.py @@ -61,12 +61,14 @@ initialSessionId = callJenkins('session-id') alreadyInstalledPlugins = callJenkins('list-plugins') plugins = [] +plugins.append('android-lint') plugins.append('build-timeout') plugins.append('categorized-view') plugins.append('cobertura') plugins.append('copyartifact') plugins.append('cppcheck') plugins.append('depgraph-view') +plugins.append('build-name-setter') plugins.append('description-setter') plugins.append('envinject') plugins.append('extra-columns') diff --git a/resources/jenkins/lcov_cobertura.py b/resources/jenkins/lcov_cobertura.py deleted file mode 100755 index 7aae6d1..0000000 --- a/resources/jenkins/lcov_cobertura.py +++ /dev/null @@ -1,414 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2011-2012 Eric Wendelin -# -# This is free software, licensed under the Apache License, Version 2.0, -# available in the accompanying LICENSE.txt file. - -""" -Converts lcov line coverage output to Cobertura-compatible XML for CI -""" - -import re -import sys -import os -import time -import subprocess -from xml.dom import minidom -from optparse import OptionParser - -from distutils.spawn import find_executable - -CPPFILT = "c++filt" -HAVE_CPPFILT = False - -if find_executable(CPPFILT) is not None: - HAVE_CPPFILT = True - -VERSION = '1.6' -__all__ = ['LcovCobertura'] - - -class Demangler(object): - def __init__(self): - self.pipe = subprocess.Popen( - CPPFILT, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - - def demangle(self, name): - self.pipe.stdin.write(name + "\n") - return self.pipe.stdout.readline().rstrip() - - -class LcovCobertura(object): - """ - Converts code coverage report files in lcov format to Cobertura's XML - report format so that CI servers like Jenkins can aggregate results and - determine build stability etc. - - >>> from lcov_cobertura import LcovCobertura - >>> LCOV_INPUT = 'your lcov input' - >>> converter = LcovCobertura(LCOV_INPUT) - >>> cobertura_xml = converter.convert() - >>> print(cobertura_xml) - """ - - def __init__(self, lcov_data, base_dir='.', excludes=None, demangle=False): - """ - Create a new :class:`LcovCobertura` object using the given `lcov_data` - and `options`. - - :param lcov_data: Path to LCOV data file - :type lcov_data: string - :param base_dir: Path upon which to base all sources - :type base_dir: string - :param excludes: list of regexes to packages as excluded - :type excludes: [string] - :param demangle: whether to demangle function names using c++filt - :type demangle: bool - """ - - if not excludes: - excludes = [] - self.lcov_data = lcov_data - self.base_dir = base_dir - self.excludes = excludes - if demangle: - demangler = Demangler() - self.format = demangler.demangle - else: - self.format = lambda x: x - - def convert(self): - """ - Convert lcov file to cobertura XML using options from this instance. - """ - coverage_data = self.parse() - return self.generate_cobertura_xml(coverage_data) - - def parse(self): - """ - Generate a data structure representing it that can be serialized in any - logical format. - """ - - coverage_data = { - 'packages': {}, - 'summary': {'lines-total': 0, 'lines-covered': 0, - 'branches-total': 0, 'branches-covered': 0}, - 'timestamp': str(int(time.time())) - } - package = None - current_file = None - file_lines_total = 0 - file_lines_covered = 0 - file_lines = {} - file_methods = {} - file_branches_total = 0 - file_branches_covered = 0 - - for line in self.lcov_data.split('\n'): - if line.strip() == 'end_of_record': - if current_file is not None: - package_dict = coverage_data['packages'][package] - package_dict['lines-total'] += file_lines_total - package_dict['lines-covered'] += file_lines_covered - package_dict['branches-total'] += file_branches_total - package_dict['branches-covered'] += file_branches_covered - file_dict = package_dict['classes'][current_file] - file_dict['lines-total'] = file_lines_total - file_dict['lines-covered'] = file_lines_covered - file_dict['lines'] = dict(file_lines) - file_dict['methods'] = dict(file_methods) - file_dict['branches-total'] = file_branches_total - file_dict['branches-covered'] = file_branches_covered - coverage_data['summary']['lines-total'] += file_lines_total - coverage_data['summary']['lines-covered'] += file_lines_covered - coverage_data['summary']['branches-total'] += file_branches_total - coverage_data['summary']['branches-covered'] += file_branches_covered - - line_parts = line.split(':', 1) - input_type = line_parts[0] - - if input_type == 'SF': - # Get file name - file_name = line_parts[-1].strip() - relative_file_name = os.path.relpath(file_name, self.base_dir) - package = '.'.join(relative_file_name.split(os.path.sep)[0:-1]) - class_name = '.'.join(relative_file_name.split(os.path.sep)) - if package not in coverage_data['packages']: - coverage_data['packages'][package] = { - 'classes': {}, 'lines-total': 0, 'lines-covered': 0, - 'branches-total': 0, 'branches-covered': 0 - } - coverage_data['packages'][package]['classes'][ - relative_file_name] = { - 'name': class_name, 'lines': {}, 'lines-total': 0, - 'lines-covered': 0, 'branches-total': 0, - 'branches-covered': 0 - } - package = package - current_file = relative_file_name - file_lines_total = 0 - file_lines_covered = 0 - file_lines.clear() - file_methods.clear() - file_branches_total = 0 - file_branches_covered = 0 - elif input_type == 'DA': - # DA:2,0 - (line_number, line_hits) = line_parts[-1].strip().split(',') - line_number = int(line_number) - if line_number not in file_lines: - file_lines[line_number] = { - 'branch': 'false', 'branches-total': 0, - 'branches-covered': 0 - } - file_lines[line_number]['hits'] = line_hits - # Increment lines total/covered for class and package - try: - if int(line_hits) > 0: - file_lines_covered += 1 - except: - pass - file_lines_total += 1 - elif input_type == 'BRDA': - # BRDA:1,1,2,0 - (line_number, block_number, branch_number, branch_hits) = line_parts[-1].strip().split(',') - line_number = int(line_number) - if line_number not in file_lines: - file_lines[line_number] = { - 'branch': 'true', 'branches-total': 0, - 'branches-covered': 0, 'hits': 0 - } - file_lines[line_number]['branch'] = 'true' - file_lines[line_number]['branches-total'] += 1 - file_branches_total += 1 - if branch_hits != '-' and int(branch_hits) > 0: - file_lines[line_number]['branches-covered'] += 1 - file_branches_covered += 1 - elif input_type == 'BRF': - file_branches_total = int(line_parts[1]) - elif input_type == 'BRH': - file_branches_covered = int(line_parts[1]) - elif input_type == 'FN': - # FN:5,(anonymous_1) - function_line, function_name = line_parts[-1].strip().split(',') - file_methods[function_name] = [function_line, '0'] - elif input_type == 'FNDA': - # FNDA:0,(anonymous_1) - (function_hits, function_name) = line_parts[-1].strip().split(',') - if function_name not in file_methods: - file_methods[function_name] = ['0', '0'] - file_methods[function_name][-1] = function_hits - - # Exclude packages - excluded = [x for x in coverage_data['packages'] for e in self.excludes - if re.match(e, x)] - for package in excluded: - del coverage_data['packages'][package] - - # Compute line coverage rates - for package_data in list(coverage_data['packages'].values()): - package_data['line-rate'] = self._percent( - package_data['lines-total'], - package_data['lines-covered']) - package_data['branch-rate'] = self._percent( - package_data['branches-total'], - package_data['branches-covered']) - - return coverage_data - - def generate_cobertura_xml(self, coverage_data): - """ - Given parsed coverage data, return a String cobertura XML representation. - - :param coverage_data: Nested dict representing coverage information. - :type coverage_data: dict - """ - - dom_impl = minidom.getDOMImplementation() - doctype = dom_impl.createDocumentType("coverage", None, - "http://cobertura.sourceforge.net/xml/coverage-04.dtd") - document = dom_impl.createDocument(None, "coverage", doctype) - root = document.documentElement - summary = coverage_data['summary'] - self._attrs(root, { - 'branch-rate': self._percent(summary['branches-total'], - summary['branches-covered']), - 'branches-covered': str(summary['branches-covered']), - 'branches-valid': str(summary['branches-total']), - 'complexity': '0', - 'line-rate': self._percent(summary['lines-total'], - summary['lines-covered']), - 'lines-covered': str(summary['lines-covered']), - 'lines-valid': str(summary['lines-total']), - 'timestamp': coverage_data['timestamp'], - 'version': '2.0.3' - }) - - sources = self._el(document, 'sources', {}) - source = self._el(document, 'source', {}) - source.appendChild(document.createTextNode(self.base_dir)) - sources.appendChild(source) - - root.appendChild(sources) - - packages_el = self._el(document, 'packages', {}) - - packages = coverage_data['packages'] - for package_name, package_data in list(packages.items()): - package_el = self._el(document, 'package', { - 'line-rate': package_data['line-rate'], - 'branch-rate': package_data['branch-rate'], - 'name': package_name, - 'complexity': '0', - }) - classes_el = self._el(document, 'classes', {}) - for class_name, class_data in list(package_data['classes'].items()): - class_el = self._el(document, 'class', { - 'branch-rate': self._percent(class_data['branches-total'], - class_data['branches-covered']), - 'complexity': '0', - 'filename': class_name, - 'line-rate': self._percent(class_data['lines-total'], - class_data['lines-covered']), - 'name': class_data['name'] - }) - - # Process methods - methods_el = self._el(document, 'methods', {}) - for method_name, (line, hits) in list(class_data['methods'].items()): - method_el = self._el(document, 'method', { - 'name': self.format(method_name), - 'signature': '', - 'line-rate': '1.0' if int(hits) > 0 else '0.0', - 'branch-rate': '1.0' if int(hits) > 0 else '0.0', - }) - method_lines_el = self._el(document, 'lines', {}) - method_line_el = self._el(document, 'line', { - 'hits': hits, - 'number': line, - 'branch': 'false', - }) - method_lines_el.appendChild(method_line_el) - method_el.appendChild(method_lines_el) - methods_el.appendChild(method_el) - - # Process lines - lines_el = self._el(document, 'lines', {}) - lines = list(class_data['lines'].keys()) - lines.sort() - for line_number in lines: - line_el = self._el(document, 'line', { - 'branch': class_data['lines'][line_number]['branch'], - 'hits': str(class_data['lines'][line_number]['hits']), - 'number': str(line_number) - }) - if class_data['lines'][line_number]['branch'] == 'true': - total = int(class_data['lines'][line_number]['branches-total']) - covered = int(class_data['lines'][line_number]['branches-covered']) - percentage = int((covered * 100.0) / total) - line_el.setAttribute('condition-coverage', - '{0}% ({1}/{2})'.format( - percentage, covered, total)) - lines_el.appendChild(line_el) - - class_el.appendChild(methods_el) - class_el.appendChild(lines_el) - classes_el.appendChild(class_el) - package_el.appendChild(classes_el) - packages_el.appendChild(package_el) - root.appendChild(packages_el) - - return document.toprettyxml() - - def _el(self, document, name, attrs): - """ - Create an element within document with given name and attributes. - - :param document: Document element - :type document: Document - :param name: Element name - :type name: string - :param attrs: Attributes for element - :type attrs: dict - """ - return self._attrs(document.createElement(name), attrs) - - def _attrs(self, element, attrs): - """ - Set attributes on given element. - - :param element: DOM Element - :type element: Element - :param attrs: Attributes for element - :type attrs: dict - """ - for attr, val in list(attrs.items()): - element.setAttribute(attr, val) - return element - - def _percent(self, lines_total, lines_covered): - """ - Get the percentage of lines covered in the total, with formatting. - - :param lines_total: Total number of lines in given module - :type lines_total: number - :param lines_covered: Number of lines covered by tests in module - :type lines_covered: number - """ - - if lines_total == 0: - return '0.0' - return str(float(float(lines_covered) / float(lines_total))) - - -def main(argv=None): - """ - Converts LCOV coverage data to Cobertura-compatible XML for reporting. - - Usage: - lcov_cobertura.py lcov-file.dat - lcov_cobertura.py lcov-file.dat -b src/dir -e test.lib -o path/out.xml - - By default, XML output will be written to ./coverage.xml - """ - if argv is None: - argv = sys.argv - parser = OptionParser() - parser.usage = ('lcov_cobertura.py lcov-file.dat [-b source/dir] ' - '[-e ] [-o output.xml] [-d]') - parser.description = 'Converts lcov output to cobertura-compatible XML' - parser.add_option('-b', '--base-dir', action='store', - help='Directory where source files are located', - dest='base_dir', default='.') - parser.add_option('-e', '--excludes', - help='Comma-separated list of regexes of packages to exclude', - action='append', dest='excludes', default=[]) - parser.add_option('-o', '--output', - help='Path to store cobertura xml file', - action='store', dest='output', default='coverage.xml') - parser.add_option('-d', '--demangle', - help='Demangle C++ function names using %s' % CPPFILT, - action='store_true', dest='demangle', default=False) - (options, args) = parser.parse_args(args=argv) - - if options.demangle and not HAVE_CPPFILT: - raise RuntimeError("C++ filter executable (%s) not found!" % CPPFILT) - - if len(args) != 2: - print(main.__doc__) - sys.exit(1) - - try: - with open(args[1], 'r') as lcov_file: - lcov_data = lcov_file.read() - lcov_cobertura = LcovCobertura(lcov_data, options.base_dir, options.excludes, options.demangle) - cobertura_xml = lcov_cobertura.convert() - with open(options.output, mode='wt') as output_file: - output_file.write(cobertura_xml) - except IOError: - sys.stderr.write("Unable to convert %s to Cobertura XML" % args[1]) - -if __name__ == '__main__': - main() diff --git a/resources/packaging/android/AndroidManifest.xml.aar.in b/resources/packaging/android/AndroidManifest.xml.aar.in index 734fc53..429bfc6 100644 --- a/resources/packaging/android/AndroidManifest.xml.aar.in +++ b/resources/packaging/android/AndroidManifest.xml.aar.in @@ -1,6 +1,25 @@ - + + + + + + + + + + + + + + + @@ -10,7 +29,7 @@ android:label="@string/app_name" android:enabled="true" android:name="com.governikus.ausweisapp2.AusweisApp2Service" - android:exported="true" + android:exported="false" > @@ -43,6 +62,7 @@ + @@ -57,17 +77,5 @@ - - - - - - - - - - - diff --git a/resources/packaging/android/AndroidManifest.xml.apk.in b/resources/packaging/android/AndroidManifest.xml.apk.in index 9e19b92..41cfdda 100644 --- a/resources/packaging/android/AndroidManifest.xml.apk.in +++ b/resources/packaging/android/AndroidManifest.xml.apk.in @@ -1,6 +1,35 @@ - + + + + + + + + + + + + + + + + + + + + + + + + @@ -75,10 +104,11 @@ + - + @@ -158,26 +188,5 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/resources/packaging/android/backup_rules.xml b/resources/packaging/android/backup_rules.xml new file mode 100644 index 0000000..9e40138 --- /dev/null +++ b/resources/packaging/android/backup_rules.xml @@ -0,0 +1,4 @@ + + + + diff --git a/resources/packaging/android/build.gradle.append b/resources/packaging/android/build.gradle.append index 19efe91..88b043c 100644 --- a/resources/packaging/android/build.gradle.append +++ b/resources/packaging/android/build.gradle.append @@ -5,7 +5,7 @@ task sourcesJar(type: Jar) { } dependencies { - compile "com.android.support:support-v4:21.0.3" + compile "com.android.support:support-v4:28.0.0" } allprojects { @@ -15,3 +15,14 @@ allprojects { } } } + +android { + packagingOptions { + exclude 'META-INF/*.version' + exclude 'META-INF/proguard/androidx-annotations.pro' + } + + lintOptions { + lintConfig file('lint.xml') + } +} diff --git a/resources/packaging/android/lint.aar.xml b/resources/packaging/android/lint.aar.xml new file mode 100644 index 0000000..0999fdc --- /dev/null +++ b/resources/packaging/android/lint.aar.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/resources/packaging/android/lint.apk.xml b/resources/packaging/android/lint.apk.xml new file mode 100644 index 0000000..8818d55 --- /dev/null +++ b/resources/packaging/android/lint.apk.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + diff --git a/resources/packaging/android/res/mipmap-anydpi-v26/npa.xml b/resources/packaging/android/res/mipmap-anydpi-v26/npa.xml new file mode 100644 index 0000000..4c7b81f --- /dev/null +++ b/resources/packaging/android/res/mipmap-anydpi-v26/npa.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/packaging/android/res/values/strings.xml b/resources/packaging/android/res/values/strings.xml new file mode 100644 index 0000000..6eacd99 --- /dev/null +++ b/resources/packaging/android/res/values/strings.xml @@ -0,0 +1,4 @@ + + + AusweisApp2 + diff --git a/resources/packaging/android/styles.xml b/resources/packaging/android/res/values/style.xml similarity index 62% rename from resources/packaging/android/styles.xml rename to resources/packaging/android/res/values/style.xml index 59ebb8e..2088b5c 100644 --- a/resources/packaging/android/styles.xml +++ b/resources/packaging/android/res/values/style.xml @@ -1,8 +1,9 @@ #dcebf6 diff --git a/resources/packaging/ios/AusweisApp2.entitlements b/resources/packaging/ios/AusweisApp2.entitlements new file mode 100644 index 0000000..2bb4dee --- /dev/null +++ b/resources/packaging/ios/AusweisApp2.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.nfc.readersession.formats + + TAG + + + diff --git a/resources/packaging/ios/MacOSXBundleInfo.plist.in b/resources/packaging/ios/MacOSXBundleInfo.plist.in index ef3e8e3..c46ea64 100644 --- a/resources/packaging/ios/MacOSXBundleInfo.plist.in +++ b/resources/packaging/ios/MacOSXBundleInfo.plist.in @@ -2,6 +2,10 @@ + com.apple.developer.nfc.readersession.iso7816.select-identifiers + + E80704007F00070302 + CFBundleDevelopmentRegion English CFBundleExecutable @@ -73,72 +77,12 @@ UIRequiresFullScreen - UILaunchImages - - - UILaunchImageMinimumOSVersion - 10.0 - UILaunchImageName - launchImage568 - UILaunchImageOrientation - Portrait - UILaunchImageSize - {320, 568} - - - UILaunchImageMinimumOSVersion - 10.0 - UILaunchImageName - launchImage667 - UILaunchImageOrientation - Portrait - UILaunchImageSize - {375, 667} - - - UILaunchImageMinimumOSVersion - 10.0 - UILaunchImageName - launchImage736 - UILaunchImageOrientation - Portrait - UILaunchImageSize - {414, 736} - - - UILaunchImages~ipad - - - UILaunchImageMinimumOSVersion - 10.0 - UILaunchImageName - launchImage1024 - UILaunchImageOrientation - Landscape - UILaunchImageSize - {768, 1024} - - - UILaunchImageMinimumOSVersion - 10.0 - UILaunchImageName - launchImage1112 - UILaunchImageOrientation - Landscape - UILaunchImageSize - {834, 1112} - - - UILaunchImageMinimumOSVersion - 10.0 - UILaunchImageName - launchImage1366 - UILaunchImageOrientation - Landscape - UILaunchImageSize - {1024, 1366} - - + UILaunchStoryboardName + launchscreen + UIStatusBarStyle + UIStatusBarStyleLightContent + UIViewControllerBasedStatusBarAppearance + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait @@ -155,6 +99,8 @@ bluetooth-central fetch + NSBluetoothAlwaysUsageDescription + AusweisApp2 needs Bluetooth to enable the required card reader. NSBluetoothPeripheralUsageDescription AusweisApp2 needs Bluetooth to enable the required card reader. NFCReaderUsageDescription diff --git a/resources/packaging/ios/de.lproj/InfoPlist.strings b/resources/packaging/ios/de.lproj/InfoPlist.strings index e000b6b..642604c 100644 --- a/resources/packaging/ios/de.lproj/InfoPlist.strings +++ b/resources/packaging/ios/de.lproj/InfoPlist.strings @@ -1,2 +1,2 @@ -NSBluetoothPeripheralUsageDescription = "Die AusweisApp2 nutzt Bluetooth, um auf das benötigte Kartenlesegerät zuzugreifen."; +NSBluetoothPeripheralUsageDescription = "Die AusweisApp2 nutzt Bluetooth, um auf den benötigten Kartenleser zuzugreifen."; NFCReaderUsageDescription = "Die AusweisApp2 nutzt NFC, um auf den Personalausweis zuzugreifen."; diff --git a/resources/packaging/ios/launchscreen.storyboard b/resources/packaging/ios/launchscreen.storyboard new file mode 100644 index 0000000..bb96682 --- /dev/null +++ b/resources/packaging/ios/launchscreen.storyboard @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/packaging/macos/Info.plist b/resources/packaging/macos/Info.plist index d092360..d9db4ec 100644 --- a/resources/packaging/macos/Info.plist +++ b/resources/packaging/macos/Info.plist @@ -34,7 +34,7 @@ LSBackgroundOnly 0 LSMinimumSystemVersion - 10.11.0 + 10.12.0 LSUIElement 1 NSHighResolutionCapable diff --git a/resources/packaging/macos/autostart_helper/Info.plist b/resources/packaging/macos/autostart_helper/Info.plist index 376b3a2..da8c744 100644 --- a/resources/packaging/macos/autostart_helper/Info.plist +++ b/resources/packaging/macos/autostart_helper/Info.plist @@ -27,7 +27,7 @@ LSBackgroundOnly 1 LSMinimumSystemVersion - 10.11.0 + 10.12.0 NSPrincipalClass NSApplication diff --git a/resources/qml/+desktop/main.qml b/resources/qml/+desktop/main.qml index 60b5267..289ff26 100644 --- a/resources/qml/+desktop/main.qml +++ b/resources/qml/+desktop/main.qml @@ -1,85 +1,229 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import Governikus.Global 1.0 import Governikus.TitleBar 1.0 import Governikus.MainView 1.0 +import Governikus.HistoryView 1.0 +import Governikus.SelfAuthenticationView 1.0 import Governikus.IdentifyView 1.0 +import Governikus.ChangePinView 1.0 import Governikus.ProviderView 1.0 +import Governikus.InformationView 1.0 +import Governikus.SettingsView 1.0 +import Governikus.TutorialView 1.0 import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 import Governikus.Type.UiModule 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.SelfAuthModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ChangePinModel 1.0 +import Governikus.Style 1.0 +import QtQml 2.10 +import QtQml.Models 2.10 import QtQuick 2.10 import QtQuick.Controls 2.3 import QtGraphicalEffects 1.0 +import Qt.labs.platform 1.1 ApplicationWindow { - readonly property int initialWidth: Utils.dp(1600) - readonly property int initialHeight: Utils.dp(1200) - id: appWindow - visible: true - width: initialWidth - height: initialHeight - title: "AusweisApp2" - background: Item { - Rectangle { - anchors.fill: parent - color: Constants.background_color - visible: plugin.useFlatStyleOnDesktop() - } - Image { - anchors.fill: parent - fillMode: Image.PreserveAspectCrop - source: visible ? "qrc:///images/desktop/background.png" : "" - visible: !plugin.useFlatStyleOnDesktop() - LinearGradient { - anchors.fill: parent - start: Qt.point(0, 0) - end: Qt.point(0, appWindow.height) - gradient: Gradient { - GradientStop { - position: 0 - color: "#AA659bcd" - } - GradientStop { - position: 1 - color: "#FF659bcd" - } - } - } - } + function openSaveFileDialog(pOnAcceptedCallback, pDefaultFilename, pSuffix) { + //: LABEL DESKTOP_QML + saveFileDialog.nameFilters = [qsTr("Text files") + " (*." + pSuffix + ")"] + saveFileDialog.defaultSuffix = pSuffix + saveFileDialog.currentFile = saveFileDialog.folder + "/" + pDefaultFilename + saveFileDialog.acceptedCallback = pOnAcceptedCallback + saveFileDialog.open() } - onWidthChanged: setScaleFactor(); - onHeightChanged: setScaleFactor(); - function setScaleFactor() { - ApplicationModel.scaleFactor = Math.min(width / initialWidth, height / initialHeight) + visible: false + width: d.initialWidth + height: d.initialHeight + + minimumWidth: 480 + minimumHeight: 360 + + title: Qt.application.name + color: Style.color.background + menuBar: TitleBar { + id: titleBar + contentRoot: contentLoader + onRootClicked: d.activeView = SectionPage.Views.Main } - property int activeView: 0 - function activateView(pName) { - if (pName < 1) { - console.warn("Unknown view requested: " + pName) - return; - } + Component.onCompleted: menuBar.forceActiveFocus() - activeView = pName - titleBar.updateActions() - } + onWidthChanged: d.setScaleFactor() + onHeightChanged: d.setScaleFactor() onClosing: { - hide() - plugin.hide(); + if (SettingsModel.remindUserToClose) { + closeWarning.open() + close.accepted = false + } else { + hide() + plugin.hide() + } + } + onVisibilityChanged: if (visibility !== ApplicationWindow.Minimized) d.lastVisibility = visibility + + QtObject { + id: d + + property int activeView: SectionPage.Views.Main + property int lastVisibility: ApplicationWindow.Windowed + readonly property int initialWidth: ApplicationModel.dpiScale * 1600 + readonly property int initialHeight: ApplicationModel.dpiScale * 1200 + + function closeOpenDialogs() { + saveFileDialog.reject() + closeWarning.close() + } + + function showMainWindow() { + var currentFlags = flags + // Force the window to the foreground if it was minimized (not closed to tray) + if (Qt.platform.os === "windows") { + flags = currentFlags | Qt.WindowStaysOnTopHint + } + + if (d.lastVisibility === ApplicationWindow.Maximized) { + showMaximized() + } else { + show() + } + + flags = currentFlags + raise() + requestActivate() + } + + function setScaleFactor() { + ApplicationModel.scaleFactor = Math.min(width / initialWidth, height / initialHeight) + } + + } + + FileDialog { + id: saveFileDialog + + property var acceptedCallback + + fileMode: FileDialog.SaveFile + folder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation) + onAccepted: acceptedCallback(file) + } + + ConfirmationPopup { + id: domination + + visible: plugin.dominated + + style: ConfirmationPopup.PopupStyle.NoButtons + //: INFO DESKTOP_QML The AA2 is currently remote controlled via the SDK interface, concurrent usage of the AA2 is not possible. + title: qsTr("Another application uses %1").arg(Qt.application.name) + text: plugin.dominator + closePolicy: Popup.NoAutoClose + } + + ConfirmationPopup { + id: closeWarning + + closePolicy: Popup.NoAutoClose + style: ConfirmationPopup.PopupStyle.OkButton + //: INFO DESKTOP_QML Header of the popup that is shown when the AA2 is closed for the first time. + title: qsTr("The user interface of the %1 is closed.").arg(Qt.application.name) + SettingsModel.translationTrigger + //: INFO DESKTOP_QML Content of the popup that is shown when the AA2 is closed for the first time. + text: qsTr("The program remains available via the icon in the system tray. Click on the %1 icon to reopen the user interface.").arg(Qt.application.name) + SettingsModel.translationTrigger + onConfirmed: { + hide() + plugin.hide() + } + + ToggleableOption { + //: LABEL DESKTOP_QML + text: qsTr("Do not show this dialog again.") + SettingsModel.translationTrigger + textStyle: Style.text.normal + + checked: !SettingsModel.remindUserToClose + + onCheckedChanged: SettingsModel.remindUserToClose = !checked + } } Connections { target: plugin onFireShowRequest: { - showNormal() - raise() - requestActivate() - if (pModule === UiModule.IDENTIFY) activateView(SectionPage.Views.Identify) + d.showMainWindow() + d.closeOpenDialogs() + switch (pModule) { + case UiModule.IDENTIFY: + if (ApplicationModel.currentWorkflow === "") { + d.activeView = SectionPage.Views.SelfAuthentication + } + if (ApplicationModel.currentWorkflow === "authentication" || ApplicationModel.currentWorkflow === "selfauthentication") { + d.activeView = SectionPage.Views.Identify + } + break + case UiModule.PINMANAGEMENT: + if (ApplicationModel.currentWorkflow === "") { + ChangePinModel.startWorkflow() + } + if (ApplicationModel.currentWorkflow === "changepin") { + d.activeView = SectionPage.Views.ChangePin + } + break + case UiModule.DEFAULT: + if (ApplicationModel.currentWorkflow === "") { + d.activeView = SectionPage.Views.Main + } + break + case UiModule.SETTINGS: + if (ApplicationModel.currentWorkflow === "") { + d.activeView = SectionPage.Views.Settings + } + break + case UiModule.CURRENT: + if (SettingsModel.showSetupAssistantOnStart) { + d.activeView = SectionPage.Views.SetupAssistant + } + break + } + } + onFireHideRequest: { + hide() + } + } + + Connections { + target: SettingsModel + onFireAppUpdateDataChanged: { + var updateData = SettingsModel.appUpdateData + + if (!updateData.valid) { + //: INFO DESKTOP_QML Message that the update data is invalid and can't be used. + ApplicationModel.showFeedback(qsTr("Unsupported version of %1.").arg(Qt.application.name)) + } + else { + if (pUpdateAvailable) { + //: INFO DESKTOP_QML An update was found which matches the current platform, the new version number is shown in the message. + ApplicationModel.showFeedback(qsTr("An update is available (Version: %1).").arg(updateData.version)) + //: INFO DESKTOP_QML An update was found. This is the caption of the download button, clicking it opens the link in the browser. + ApplicationModel.showFeedback("".arg(updateData.url) + qsTr("Download") + "") + //: INFO DESKTOP_QML An update was found. This is the caption of the release note button, clicking it opens the note in the browser. + ApplicationModel.showFeedback("".arg(updateData.notesUrl) + qsTr("Release notes") + "") + } + else { + //: INFO DESKTOP_QML The AA2 is up-to-date, this message is only shown if the update check is started by the user and not via the auto-update functionality. + ApplicationModel.showFeedback(qsTr("Current version %1 is up to date.").arg(updateData.currentVersion)) + } + } } } @@ -88,52 +232,90 @@ ApplicationWindow { onTriggered: plugin.developerBuild ? plugin.doRefresh() : "" } - menuBar: TitleBar { - id: titleBar - contentRoot: contentArea - onRootClicked: activateView(SectionPage.Views.Main) - navSuccessor: { - if (viewMain.visible) { - return viewMain - } - if (viewIdentify.visible) { - return viewIdentify.tabTarget - } - if (viewProvider.visible) { - return viewProvider - } - return null - } + Shortcut { + sequence: StandardKey.HelpContents + onActivated: ApplicationModel.openOnlineHelp("index") } - Item { - id: contentArea + Loader { + id: contentLoader + + // Keep in sync with the order/length of the SectionPage.Views enum + property var sectionPages: ObjectModel { + Component {MainView {}} + Component {SelfAuthenticationView {}} + Component {IdentifyView {}} + Component {ChangePinView {}} + Component {ProviderView {}} + Component {InformationView {}} + Component {SettingsView {}} + Component {HistoryView {}} + Component {SetupAssistantView {}} + } + + Keys.onEscapePressed: { + if (d.activeView !== SectionPage.Views.Main) { + d.activeView = SectionPage.Views.Main + } + } + + Connections { + target: contentLoader.item + onNextView: d.activeView = pName + onVisibleChildrenChanged: titleBar.updateActions() + } + anchors.fill: parent - MainView { - id: viewMain + onItemChanged: titleBar.updateActions() + sourceComponent: sectionPages.get(d.activeView) + } - visible: appWindow.activeView === 0 || appWindow.activeView === SectionPage.Views.Main - onVisibleChildrenChanged: titleBar.updateActions() - onNextView: activateView(pName) - navSuccessor: titleBar + Rectangle { + visible: SettingsModel.developerMode && d.activeView !== SectionPage.Views.Settings + height: childrenRect.height + width: childrenRect.width + anchors { + bottom: parent.bottom + bottomMargin: 4 + right: parent.right + rightMargin: 4 } - IdentifyView { - id: viewIdentify + color: Constants.white + opacity: 0.7 + radius: ApplicationModel.scaleFactor * 4 - visible: appWindow.activeView === SectionPage.Views.Identify - onNextView: activateView(pName) - navSuccessor: titleBar - } - - ProviderView { - id: viewProvider - - visible: appWindow.activeView === SectionPage.Views.Provider - onVisibleChildrenChanged: titleBar.updateActions() - onNextView: activateView(pName) - navSuccessor: titleBar + Row { + spacing: Constants.groupbox_spacing + padding: Constants.pane_padding / 2 + Label { + //: LABEL DESKTOP_QML + text: qsTr("Developer Mode: Enabled!") + SettingsModel.translationTrigger + color: Constants.red + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: Style.dimens.normal_font_size + } + GButton { + //: LABEL DESKTOP_QML Global button to disable developer mode. + text: qsTr("Disable") + SettingsModel.translationTrigger + onClicked: SettingsModel.developerMode = false + } } } + + Rectangle { + id: developerWarning + + visible: SettingsModel.developerMode && d.activeView !== SectionPage.Views.Settings + height: ApplicationModel.scaleFactor * 50 + width: Math.sqrt(contentLoader.width * contentLoader.width + contentLoader.height * contentLoader.height) + anchors.verticalCenter: parent.bottom + + color: Constants.red + opacity: 0.5 + rotation: -Math.atan(contentLoader.height / contentLoader.width) * 180 / Math.PI + transformOrigin: Item.Left + antialiasing: true + } } diff --git a/resources/qml/+mobile/main.qml b/resources/qml/+mobile/main.qml index 7665133..315b5f1 100644 --- a/resources/qml/+mobile/main.qml +++ b/resources/qml/+mobile/main.qml @@ -1,6 +1,9 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 -import QtQuick.Window 2.2 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 @@ -9,16 +12,80 @@ import Governikus.SplashScreen 1.0 import Governikus.View 1.0 import Governikus.FeedbackView 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Style 1.0 ApplicationWindow { id: appWindow + visible: true width: 750 / 2 //Screen.desktopAvailableWidth height: 1334 / 2 - title: "Governikus AusweisApp2" - color: Constants.background_color - property var lastCloseInvocation: 0 + title: "Governikus AusweisApp2" + color: Style.color.background + Component.onCompleted: { + flags |= Qt.MaximizeUsingFullscreenGeometryHint + } + + menuBar: TitleBar { + id: titleBar + + property var currentSectionPage: if (contentArea) contentArea.currentSectionPage + + visible: !splashScreen.visible && (!currentSectionPage || currentSectionPage.titleBarVisible) + + navigationAction: currentSectionPage ? currentSectionPage.navigationAction : null + title: currentSectionPage ? currentSectionPage.title : "" + rightAction: currentSectionPage ? currentSectionPage.rightTitleBarAction : null + subTitleBarAction: currentSectionPage ? currentSectionPage.subTitleBarAction : null + color: currentSectionPage ? currentSectionPage.titleBarColor : null + titleBarOpacity: contentArea.visibleItem && contentArea.visibleItem.stack.currentItem ? contentArea.visibleItem.stack.currentItem.titleBarOpacity : 1 + } + + onClosing: { // back button pressed + if (contentArea.visibleItem) + { + var activeStackView = contentArea.visibleItem.stack + var navigationAction = activeStackView.currentItem.navigationAction + + if (activeStackView.depth <= 1 + && (!navigationAction || navigationAction.state === "")) { + if (contentArea.state === "identify" + || SettingsModel.showSetupAssistantOnStart) { // Don't go back to "identify" section page when setup tutorial is active + var currentTime = new Date().getTime(); + if( currentTime - d.lastCloseInvocation < 1000 ) { + plugin.fireQuitApplicationRequest() + return + } + + d.lastCloseInvocation = currentTime + //: INFO ANDROID IOS Hint that is shown if the users pressed the "back" button on the top-most navigation level for the first time (a second press closes the app). + ApplicationModel.showFeedback(qsTr("To close the app, quickly press the back button twice.")) + } else { + navBar.state = "identify" + navBar.currentIndex = 0 + navBar.lockedAndHidden = false + } + } + else if (navigationAction) { + if (navBar.isOpen) { + navBar.close() + } + else if (navigationAction.state !== "hidden") { + navigationAction.clicked(undefined) + } + } + } + + close.accepted = false + } + + QtObject { + id: d + + property var lastCloseInvocation: 0 + } Action { shortcut: "Ctrl+Alt+R" @@ -30,32 +97,34 @@ ApplicationWindow { onTriggered: appWindow.close() } - menuBar: TitleBar { - id: titleBar - visible: !splashScreen.visible && contentArea.state !== "tutorial" - - titleBarOpacity: contentArea.visibleItem && contentArea.visibleItem.stack.currentItem ? contentArea.visibleItem.stack.currentItem.titleBarOpacity : 1 - - property var currentSectionPage: if (contentArea) contentArea.currentSectionPage - - leftAction: if (currentSectionPage) currentSectionPage.leftTitleBarAction - titleItem: if (currentSectionPage) currentSectionPage.headerTitleBarAction - rightAction: if (currentSectionPage) currentSectionPage.rightTitleBarAction - subTitleBarAction: if (currentSectionPage) currentSectionPage.subTitleBarAction - color: if (currentSectionPage) currentSectionPage.titleBarColor + Connections { + target: plugin + enabled: contentArea.ready + onFireApplicationActivated: feedback.showIfNecessary() } ContentAreaLoader { id: contentArea - state: navBar.state - anchors.left: Constants.leftNavigation ? navBar.right : parent.left - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: Constants.bottomNavigation ? navBar.top : parent.bottom + anchors { + left: Constants.leftNavigation ? navBar.right : parent.left + top: parent.top + right: parent.right + bottom: Constants.bottomNavigation ? navBar.top : parent.bottom + bottomMargin: ( + currentSectionPage && currentSectionPage.automaticSafeAreaMarginHandling && + (!Constants.bottomNavigation || navBar.lockedAndHidden) + ) ? plugin.safeAreaMargins.bottom : 0 + + Behavior on bottomMargin { + NumberAnimation {duration: Constants.animation_duration} + } + } + + state: navBar.state onReadyChanged: { splashScreen.hide() - if (!ApplicationModel.currentWorkflow && !settingsModel.showTutorialOnStart) { + if (!ApplicationModel.currentWorkflow && !SettingsModel.showSetupAssistantOnStart) { navBar.lockedAndHidden = false } @@ -63,74 +132,47 @@ ApplicationWindow { } } + SplashScreen { + id: splashScreen + + color: appWindow.color + } + Navigation { id: navBar + visible: !splashScreen.visible anchors.left: parent.left anchors.top: Constants.leftNavigation ? parent.top : undefined anchors.right: Constants.bottomNavigation ? parent.right : undefined anchors.bottom: parent.bottom + + onReselectedState: contentArea.reselectedState() } - onClosing: { // back button pressed - if (contentArea.visibleItem) - { - var activeStackView = contentArea.visibleItem.stack - var leftTitleBarAction = activeStackView.currentItem.leftTitleBarAction + ConfirmationPopup { + id: toast - if (activeStackView.depth <= 1 - && (!leftTitleBarAction || leftTitleBarAction.state === "") - && contentArea.state != "tutorial") // Don't go back to "identify" section page when tutorial is active - { - if (contentArea.state != "identify") { - navBar.state = "identify" - navBar.currentIndex = 0 - } else { - var currentTime = new Date().getTime(); - if( currentTime - lastCloseInvocation < 1000 ) { - plugin.fireQuitApplicationRequest() - return - } + visible: ApplicationModel.feedback !== "" - lastCloseInvocation = currentTime - qmlExtension.showFeedback(qsTr("To close the app, quickly press the back button twice.")) - } - } - else if (leftTitleBarAction) { - if (navBar.isOpen) { - navBar.close() - } - else if (leftTitleBarAction.state !== "hidden") { - leftTitleBarAction.clicked(undefined) - } - } - } + style: ApplicationModel.isScreenReaderRunning() ? ConfirmationPopup.PopupStyle.OkButton : ConfirmationPopup.PopupStyle.NoButtons + closePolicy: Popup.NoAutoClose + modal: ApplicationModel.isScreenReaderRunning() + dim: true - close.accepted = false + text: ApplicationModel.feedback + + onConfirmed: ApplicationModel.onShowNextFeedback() } StoreFeedbackPopup { id: feedback - x: (appWindow.width - width) / 2 - y: (appWindow.height - height) / 2 - function showIfNecessary() { - if (ApplicationModel.areStoreFeedbackDialogConditionsMet()) { - ApplicationModel.hideFutureStoreFeedbackDialogs() + if (!ApplicationModel.currentWorkflow && SettingsModel.requestStoreFeedback()) { + SettingsModel.hideFutureStoreFeedbackDialogs() feedback.open() } } } - - Connections { - target: plugin - enabled: contentArea.ready - onFireApplicationActivated: feedback.showIfNecessary() - } - - SplashScreen { - id: splashScreen - color: appWindow.color - } } diff --git a/resources/qml/Governikus/ChangePinView/+desktop/ChangePinController.qml b/resources/qml/Governikus/ChangePinView/+desktop/ChangePinController.qml new file mode 100644 index 0000000..cb6d881 --- /dev/null +++ b/resources/qml/Governikus/ChangePinView/+desktop/ChangePinController.qml @@ -0,0 +1,99 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.ChangePinModel 1.0 +import Governikus.Type.NumberModel 1.0 + + +Controller { + id: controller + + enum WorkflowStates { + Initial, + Reader, + Card, + Update, + Password, + Processing + } + + + property int workflowState: ChangePinController.WorkflowStates.Initial + + QtObject { + id: d + + property bool showRemoveCardFeedback: false + } + + Connections { + target: ChangePinModel + + onFireCurrentStateChanged: processStateChange() + // This is necessary because onCurrentStateChanged is not + // working, when we need to process a state a second time. + } + + function processStateChange() { + switch (ChangePinModel.currentState) { + case "Initial": + ChangePinModel.setInitialPluginType() + break + case "StateSelectReader": + controller.nextView(ChangePinView.SubViews.Workflow) + setPinWorkflowStateAndContinue(ChangePinController.WorkflowStates.Reader) + break + case "StateConnectCard": + setPinWorkflowStateAndContinue(ChangePinController.WorkflowStates.Card) + break + case "StateEnterPacePassword": + setPinWorkflowStateAndRequestInput(ChangePinController.WorkflowStates.Password) + break + case "StateUnfortunateCardPosition": + controller.nextView(ChangePinView.SubViews.CardPosition) + break + case "StateEnterNewPacePin": + NumberModel.requestNewPin() + setPinWorkflowStateAndRequestInput(ChangePinController.WorkflowStates.Password) + break + case "StateCleanUpReaderManager": + d.showRemoveCardFeedback = ChangePinModel.selectedReaderHasCard() && !ChangePinModel.error; + setPinWorkflowStateAndContinue(ChangePinController.WorkflowStates.Processing) + break + case "FinalState": + if (d.showRemoveCardFeedback) { + d.showRemoveCardFeedback = false + //: INFO DESKTOP_QML Changing the PIN was successful; hint that the id card may now be removed from the card reader. + ApplicationModel.showFeedback(qsTr("You may now remove your ID card from the device.")) + } + if (ChangePinModel.shouldSkipResultView()) { + controller.nextView(ChangePinView.SubViews.ReturnToMain) + ChangePinModel.continueWorkflow() + } else { + controller.nextView(ChangePinView.SubViews.Result) + } + break + default: + ChangePinModel.continueWorkflow() + } + } + + function setPinWorkflowStateAndContinue(pState) { + controller.workflowState = pState + ChangePinModel.continueWorkflow() + } + + function setPinWorkflowStateAndRequestInput(pState) { + controller.workflowState = pState + if (ChangePinModel.isBasicReader) { + controller.nextView(ChangePinView.SubViews.Password) + } else { + ChangePinModel.continueWorkflow() + } + } +} diff --git a/resources/qml/Governikus/ChangePinView/+desktop/ChangePinView.qml b/resources/qml/Governikus/ChangePinView/+desktop/ChangePinView.qml new file mode 100644 index 0000000..a8d301b --- /dev/null +++ b/resources/qml/Governikus/ChangePinView/+desktop/ChangePinView.qml @@ -0,0 +1,235 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.EnterPasswordView 1.0 +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.ProgressView 1.0 +import Governikus.ResultView 1.0 +import Governikus.SettingsView 1.0 +import Governikus.View 1.0 +import Governikus.Workflow 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ChangePinModel 1.0 +import Governikus.Type.NumberModel 1.0 + + +SectionPage { + id: baseItem + + enum SubViews { + Undefined, + Workflow, + Password, + PasswordInfo, + Progress, + CardPosition, + InputError, + Data, + PinUnlocked, + Result, + ReturnToMain, + ReaderSettings + } + + Keys.onEscapePressed: if (d.cancelAllowed) ChangePinModel.cancelWorkflow() + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("PIN Management") + SettingsModel.translationTrigger + rootEnabled: false + helpTopic: "pinTab" + showSettings: (changePinController.workflowState === ChangePinController.WorkflowStates.Initial || + changePinController.workflowState === ChangePinController.WorkflowStates.Reader || + changePinController.workflowState === ChangePinController.WorkflowStates.Card) + && d.activeView !== ChangePinView.SubViews.Progress + + onClicked: { + if (d.activeView === ChangePinView.SubViews.PasswordInfo) { + d.view = ChangePinView.SubViews.Password + ApplicationWindow.menuBar.updateActions() + } + else if (d.activeView === ChangePinView.SubViews.ReaderSettings) { + d.view = readerView.preceedingView + ApplicationWindow.menuBar.updateActions() + } + } + + customSubAction: CancelAction { + visible: d.cancelAllowed + + onClicked: { + if (pinResult.visible) { + ChangePinModel.continueWorkflow() + baseItem.nextView(SectionPage.Views.Main) + } else { + ChangePinModel.cancelWorkflow() + } + } + } + + customSettingsHandler: function(){ + readerView.preceedingView = d.activeView + d.view = ChangePinView.SubViews.ReaderSettings + ApplicationWindow.menuBar.updateActions() + } + } + + QtObject { + id: d + + property var view: ChangePinView.SubViews.Undefined + readonly property int activeView: inputError.visible ? ChangePinView.SubViews.InputError : pinUnlocked.visible ? ChangePinView.SubViews.PinUnlocked : view + readonly property bool cancelAllowed: ChangePinModel.isBasicReader || generalWorkflow.waitingFor != Workflow.WaitingFor.Password + } + + TabbedReaderView { + id: readerView + + property int preceedingView: ChangePinView.SubViews.Undefined + + visible: d.activeView === ChangePinView.SubViews.ReaderSettings + onCloseView: { + d.view = preceedingView + ApplicationWindow.menuBar.updateActions() + } + } + + ChangePinController { + id: changePinController + + onNextView: { + if (pName === ChangePinView.SubViews.ReturnToMain) { + baseItem.nextView(SectionPage.Views.Main) + return; + } + + d.view = pName + } + } + + GeneralWorkflow { + id: generalWorkflow + + visible: d.activeView === ChangePinView.SubViews.Workflow + + isPinChange: true + waitingFor: switch (changePinController.workflowState) { + case ChangePinController.WorkflowStates.Reader: + return Workflow.WaitingFor.Reader + case ChangePinController.WorkflowStates.Card: + return Workflow.WaitingFor.Card + case ChangePinController.WorkflowStates.Password: + return Workflow.WaitingFor.Password + default: + return Workflow.WaitingFor.None + } + } + + EnterPasswordView { + id: enterPasswordView + + visible: d.activeView === ChangePinView.SubViews.Password + + onPasswordEntered: { + d.view = ChangePinView.SubViews.Progress + ChangePinModel.continueWorkflow() + } + + onChangePinLength: { + NumberModel.requestTransportPin = !NumberModel.requestTransportPin + } + + onRequestPasswordInfo: { + d.view = ChangePinView.SubViews.PasswordInfo + ApplicationWindow.menuBar.updateActions() + } + } + + PasswordInfoView { + id: passwordInfoView + + visible: d.activeView === ChangePinView.SubViews.PasswordInfo + + onClose: { + d.view = ChangePinView.SubViews.Password + ApplicationWindow.menuBar.updateActions() + } + } + + ProgressView { + id: pinProgressView + + visible: d.activeView === ChangePinView.SubViews.Progress + + //: LABEL DESKTOP_QML + text: qsTr("Change PIN") + SettingsModel.translationTrigger + //: INFO DESKTOP_QML Processing screen text while the card communication is running after the PIN has been entered during PIN change process. + subText: qsTr("Please wait a moment...") + SettingsModel.translationTrigger + } + + ResultView { + id: inputError + + property bool errorConfirmed: false + + visible: !errorConfirmed && NumberModel.hasPasswordError && d.view != ChangePinView.SubViews.Result + + resultType: ResultView.Type.IsError + text: NumberModel.inputError + onNextView: errorConfirmed = true + + Connections { + target: NumberModel + onFireInputErrorChanged: inputError.errorConfirmed = false + } + } + + ResultView { + id: pinUnlocked + + property bool confirmed: true + + visible: !confirmed && (d.view === ChangePinView.SubViews.Password || generalWorkflow.waitingFor === Workflow.WaitingFor.Password) + + resultType: ResultView.Type.IsSuccess + //: INFO DESKTOP_QML The ID card has just been unblocked and the user can now continue with their PIN change. + text: qsTr("Your ID card is unblocked. You now have three more tries to change your PIN") + SettingsModel.translationTrigger + onNextView: confirmed = true + + Connections { + target: ChangePinModel + onFireOnPinUnlocked: pinUnlocked.confirmed = false + } + } + + ResultView { + id: cardPositionView + + visible: d.activeView === ChangePinView.SubViews.CardPosition + + resultType: ResultView.Type.IsInfo + //: INFO DESKTOP_QML The NFC signal is weak or unstable, the user is asked to change the card's position to (hopefully) reduce the distance to the NFC chip. + text: qsTr("Weak NFC signal.\nPlease reposition your card.") + SettingsModel.translationTrigger + onNextView: ChangePinModel.continueWorkflow() + } + + ResultView { + id: pinResult + + visible: d.activeView === ChangePinView.SubViews.Result + + resultType: ChangePinModel.error ? ResultView.Type.IsError : ResultView.Type.IsSuccess + text: ChangePinModel.resultString + onNextView: { + ChangePinModel.continueWorkflow() + baseItem.nextView(pName) + } + emailButtonVisible: ChangePinModel.error && !ChangePinModel.isCancellationByUser() + onEmailButtonPressed: ChangePinModel.sendResultMail() + } +} diff --git a/resources/qml/Governikus/ChangePinView/+ios/ChangePinViewContent.qml b/resources/qml/Governikus/ChangePinView/+ios/ChangePinViewContent.qml deleted file mode 100644 index f55910f..0000000 --- a/resources/qml/Governikus/ChangePinView/+ios/ChangePinViewContent.qml +++ /dev/null @@ -1,64 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 -import Governikus.Type.ChangePinModel 1.0 - - -Item { - readonly property int spacing: (height - pinHeader.height - pinDesc.height - pinIcon.height - govButton.height - Utils.dp(40)) / 3 - - Text { - id: pinHeader - - text: qsTr("PIN Management") + settingsModel.translationTrigger - - anchors.top: parent.top - anchors.topMargin: spacing - anchors.horizontalCenter: parent.horizontalCenter - - font.pixelSize: Constants.header_font_size - color: Constants.blue - } - - Text { - id: pinDesc - color: Constants.secondary_text - - width: parent.width * 0.9 - - text: qsTr("You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority.") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - - anchors.top: pinHeader.bottom - anchors.topMargin: Utils.dp(10) - anchors.horizontalCenter: parent.horizontalCenter - } - - Image { - id: pinIcon - - height: parent.height * 0.25 - width: height - - anchors.top: pinDesc.bottom - anchors.topMargin: spacing - anchors.horizontalCenter: parent.horizontalCenter - - fillMode: Image.PreserveAspectFit - smooth: true - source: "qrc:///images/icon_Pin.svg" - } - - GButton { - id: govButton - - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottomMargin: Utils.dp(30) - - text: qsTr("Change PIN now") + settingsModel.translationTrigger - onClicked: ChangePinModel.startWorkflow() - } -} diff --git a/resources/qml/Governikus/ChangePinView/ChangePinController.qml b/resources/qml/Governikus/ChangePinView/+mobile/ChangePinController.qml similarity index 80% rename from resources/qml/Governikus/ChangePinView/ChangePinController.qml rename to resources/qml/Governikus/ChangePinView/+mobile/ChangePinController.qml index 1b0cd8d..50b21ee 100644 --- a/resources/qml/Governikus/ChangePinView/ChangePinController.qml +++ b/resources/qml/Governikus/ChangePinView/+mobile/ChangePinController.qml @@ -1,10 +1,13 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Type.ApplicationModel 1.0 import Governikus.Type.ChangePinModel 1.0 import Governikus.Type.NumberModel 1.0 import Governikus.Type.ReaderPlugIn 1.0 -import Governikus.Type.PacePasswordId 1.0 Item { @@ -69,7 +72,7 @@ Item { function processStateChange() { switch (currentState) { - case "": + case "Initial": break case "StateSelectReader": fireReplace(pinWorkflow) @@ -89,17 +92,19 @@ Item { setPinWorkflowStateAndContinue(ChangePinController.WorkflowStates.Update) break case "StateEnterPacePassword": - if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_PIN) { + if (NumberModel.passwordType === NumberModel.PASSWORD_PIN) { setPinWorkflowStateAndRequestInput(ChangePinController.WorkflowStates.Pin, "PIN") } - else if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_CAN) { + else if (NumberModel.passwordType === NumberModel.PASSWORD_CAN) { setPinWorkflowStateAndRequestInput(ChangePinController.WorkflowStates.Can, "CAN") } - else if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_PUK) { + else if (NumberModel.passwordType === NumberModel.PASSWORD_PUK) { setPinWorkflowStateAndRequestInput(ChangePinController.WorkflowStates.Puk, "PUK") } break case "StateUnfortunateCardPosition": + //: INFO IOS The NFC signal is weak or unstable. The scan is stopped with this information in the iOS dialog. + ApplicationModel.stopNfcScanWithError(qsTr("Weak NFC signal") + SettingsModel.translationTrigger) baseItem.firePush(cardPositionView) break case "StateEnterNewPacePin": @@ -112,7 +117,14 @@ Item { case "FinalState": if (controller.showRemoveCardFeedback) { controller.showRemoveCardFeedback = false - qmlExtension.showFeedback(qsTr("You may now remove your ID card from the device.")) + //: INFO ANDROID IOS Hint that the id card may be removed from the card reader since the PIN was changed successfully. + ApplicationModel.showFeedback(qsTr("You may now remove your ID card from the device.")) + } + if (ChangePinModel.shouldSkipResultView()) { + ChangePinModel.continueWorkflow() + firePopAll() + navBar.lockedAndHidden = false + break } baseItem.firePush(pinResult) navBar.lockedAndHidden = true @@ -132,6 +144,7 @@ Item { if (ChangePinModel.isBasicReader) { enterPinView.state = pInput baseItem.firePush(enterPinView) + ApplicationModel.nfcRunning = false } else { ChangePinModel.continueWorkflow() } diff --git a/resources/qml/Governikus/ChangePinView/ChangePinView.qml b/resources/qml/Governikus/ChangePinView/+mobile/ChangePinView.qml similarity index 57% rename from resources/qml/Governikus/ChangePinView/ChangePinView.qml rename to resources/qml/Governikus/ChangePinView/+mobile/ChangePinView.qml index 143874c..514b0d5 100644 --- a/resources/qml/Governikus/ChangePinView/ChangePinView.qml +++ b/resources/qml/Governikus/ChangePinView/+mobile/ChangePinView.qml @@ -1,12 +1,19 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 -import Governikus.EnterPinView 1.0 +import Governikus.EnterPasswordView 1.0 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.ProgressView 1.0 import Governikus.ResultView 1.0 import Governikus.View 1.0 import Governikus.Workflow 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ChangePinModel 1.0 import Governikus.Type.NumberModel 1.0 @@ -14,14 +21,20 @@ import Governikus.Type.NumberModel 1.0 SectionPage { id: baseItem - disableFlicking: true - headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID IOS + title: qsTr("PIN Management") + SettingsModel.translationTrigger ChangePinController { id: changePinController } - content: ChangePinViewContent { + Connections { + target: ChangePinModel + //: INFO ANDROID IOS The ID card has just been unblocked and the user can now continue with their PIN change. + onFireOnPinUnlocked: ApplicationModel.showFeedback(qsTr("Your ID card is unblocked. You now have three more tries to change your PIN")) + } + + ChangePinViewContent { height: baseItem.height width: baseItem.width } @@ -32,7 +45,8 @@ SectionPage { controller: changePinController workflowModel: ChangePinModel - workflowTitle: qsTr("PIN Management") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + workflowTitle: qsTr("PIN Management") + SettingsModel.translationTrigger waitingFor: switch (changePinController.workflowState) { case ChangePinController.WorkflowStates.Reader: @@ -51,10 +65,13 @@ SectionPage { ResultView { id: cardPositionView - headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID IOS + title: qsTr("PIN Management") + SettingsModel.translationTrigger resultType: ResultView.Type.IsInfo - buttonText: qsTr("Retry") + settingsModel.translationTrigger - text: qsTr("Weak NFC signal. Please reposition your card.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + buttonText: qsTr("Retry") + SettingsModel.translationTrigger + //: INFO ANDROID IOS The NFC signal is weak or unstable, the user is asked to change the card's position to (hopefully) reduce the distance to the NFC chip. + text: qsTr("Weak NFC signal. Please reposition your card.") + SettingsModel.translationTrigger onClicked: { firePop() ChangePinModel.continueWorkflow() @@ -64,7 +81,8 @@ SectionPage { ResultView { id: pinResult - headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID IOS + title: qsTr("PIN Management") + SettingsModel.translationTrigger resultType: ChangePinModel.error ? ResultView.Type.IsError : ResultView.Type.IsSuccess text: ChangePinModel.resultString onClicked: { @@ -75,13 +93,14 @@ SectionPage { visible: false } - EnterPinView { + EnterPasswordView { id: enterPinView - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: ChangePinModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Change PIN") + settingsModel.translationTrigger } + navigationAction: NavigationAction { state: "cancel"; onClicked: ChangePinModel.cancelWorkflow() } + //: LABEL ANDROID IOS + title: qsTr("Change PIN") + SettingsModel.translationTrigger visible: false - onPinEntered: { + onPasswordEntered: { firePop() ChangePinModel.continueWorkflow() } @@ -91,23 +110,31 @@ SectionPage { ProgressView { id: pinProgressView - leftTitleBarAction: TitleBarAction { state: ChangePinModel.isBasicReader ? "cancel" : "hidden"; onClicked: ChangePinModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("PIN Management") + settingsModel.translationTrigger; font.bold: true } + navigationAction: NavigationAction { state: ChangePinModel.isBasicReader ? "cancel" : "hidden"; onClicked: ChangePinModel.cancelWorkflow() } + //: LABEL ANDROID IOS + title: qsTr("PIN Management") + SettingsModel.translationTrigger visible: false - text: qsTr("Change PIN") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Change PIN") + SettingsModel.translationTrigger subText: (!visible ? "" + //: INFO ANDROID IOS Loading screen during PIN change process, data communcation is currently ongoing. Message is usually not visible since the password handling with basic reader is handled by EnterPasswordView. : ChangePinModel.isBasicReader ? qsTr("Please wait a moment...") : !!NumberModel.inputError ? NumberModel.inputError + //: INFO ANDROID IOS The card communcation was aborted, the online identification functionality is deactivated and needs to be actived by the authorities. : NumberModel.pinDeactivated ? qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") : changePinController.workflowState === ChangePinController.WorkflowStates.Update || changePinController.workflowState === ChangePinController.WorkflowStates.Pin + //: INFO ANDROID IOS Either an comfort card reader or smartphone-as-card-reader is used, the user needs to react to request on that device. || changePinController.workflowState === ChangePinController.WorkflowStates.NewPin ? qsTr("Please observe the display of your card reader.") + //: INFO ANDROID IOS The wrong PIN was entered twice, the next attempt requires additional verifcation via CAN. : changePinController.workflowState === ChangePinController.WorkflowStates.Can ? qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.") + //: INFO ANDROID IOS The PIN (including the CAN) was entered wrongfully three times, the PUK is required to unlock the id card. : changePinController.workflowState === ChangePinController.WorkflowStates.Puk ? qsTr("You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking.") - : qsTr("Please wait a moment...")) + settingsModel.translationTrigger + //: INFO ANDROID IOS Generic progress message during PIN change process. + : qsTr("Please wait a moment...")) + SettingsModel.translationTrigger subTextColor: !ChangePinModel.isBasicReader && (NumberModel.inputError || NumberModel.pinDeactivated || changePinController.workflowState === ChangePinController.WorkflowStates.Can - || changePinController.workflowState === ChangePinController.WorkflowStates.Puk) ? Constants.red : Constants.secondary_text + || changePinController.workflowState === ChangePinController.WorkflowStates.Puk) ? Constants.red : Style.color.secondary_text } } diff --git a/resources/qml/Governikus/ChangePinView/+android/ChangePinViewContent.qml b/resources/qml/Governikus/ChangePinView/+mobile/ChangePinViewContent.qml similarity index 58% rename from resources/qml/Governikus/ChangePinView/+android/ChangePinViewContent.qml rename to resources/qml/Governikus/ChangePinView/+mobile/ChangePinViewContent.qml index 56d19e0..b10d0b3 100644 --- a/resources/qml/Governikus/ChangePinView/+android/ChangePinViewContent.qml +++ b/resources/qml/Governikus/ChangePinView/+mobile/ChangePinViewContent.qml @@ -1,11 +1,17 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ChangePinModel 1.0 Item { - readonly property int spacing: (height - pinIcon.height - pinHeader.height - pinDesc.height - govButton.height - Utils.dp(40)) / 3 + readonly property int spacing: (height - pinIcon.height - pinHeader.height - pinDesc.height - govButton.height - 2 * Constants.pane_padding) / 3 Image { id: pinIcon @@ -22,33 +28,32 @@ Item { source: "qrc:///images/icon_Pin.svg" } - Text { + GText { id: pinHeader - text: qsTr("PIN Management") + settingsModel.translationTrigger - anchors.top: pinIcon.bottom anchors.topMargin: spacing anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: Constants.header_font_size - color: Constants.blue + //: LABEL ANDROID IOS + text: qsTr("PIN Management") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + visible: Constants.is_layout_android } - Text { + GText { id: pinDesc - color: Constants.secondary_text - anchors.margins: Utils.dp(10) + anchors.margins: Constants.pane_padding anchors.top: pinHeader.bottom - anchors.topMargin: Utils.dp(10) anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority.") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size + //: LABEL ANDROID IOS + text: qsTr("You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority.") + SettingsModel.translationTrigger + textStyle: Style.text.normal + horizontalAlignment: Text.AlignHCenter - width: parent.width - Utils.dp(60) - wrapMode: Text.WordWrap + width: Math.min(parent.width - 2 * Constants.pane_padding, Style.dimens.max_text_width) } GButton { @@ -56,9 +61,10 @@ Item { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter - anchors.bottomMargin: Utils.dp(30) + anchors.bottomMargin: Constants.pane_padding - text: qsTr("Change PIN now") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Change PIN now") + SettingsModel.translationTrigger onClicked: ChangePinModel.startWorkflow() } } diff --git a/resources/qml/Governikus/DeveloperView/DeveloperView.qml b/resources/qml/Governikus/DeveloperView/DeveloperView.qml index fb07abf..c77483e 100644 --- a/resources/qml/Governikus/DeveloperView/DeveloperView.qml +++ b/resources/qml/Governikus/DeveloperView/DeveloperView.qml @@ -1,15 +1,22 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 SectionPage { id: root - leftTitleBarAction: TitleBarAction { state: !topLevelPage ? "back" : ""; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Developer options") + settingsModel.translationTrigger; font.bold: true } + navigationAction: NavigationAction { state: !topLevelPage ? "back" : ""; onClicked: firePop() } + //: LABEL ALL_PLATFORMS + title: qsTr("Developer options") + SettingsModel.translationTrigger content: Column { id: mainColumn @@ -31,33 +38,35 @@ SectionPage { anchors.right: testUriSwitch.left anchors.rightMargin: Constants.component_spacing anchors.verticalCenter: testUriContainer.verticalCenter - Text { + + GText { id: testUriNameText - anchors.bottomMargin: Utils.dp(2) - font.pixelSize: Utils.dp(16) - color: Constants.secondary_text - opacity: 0.87 - text: qsTr("Test environment") + settingsModel.translationTrigger + anchors.bottomMargin: 2 + //: LABEL ALL_PLATFORMS + text: qsTr("Test environment") + SettingsModel.translationTrigger + textStyle: Style.text.normal_accent } - Text { + GText { id: testUriDateText width: parent.width anchors.top: testUriNameText.bottom - font.pixelSize: Utils.dp(14) - color: Constants.secondary_text - opacity: 0.38 - text: qsTr("Use the test environment during a selfauthentication") + settingsModel.translationTrigger - wrapMode: Text.WordWrap + //: LABEL ALL_PLATFORMS + text: qsTr("Use the test environment during a self-authentication") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } } GSwitch { id: testUriSwitch + anchors.right: testUriContainer.right anchors.verticalCenter: testUriContainer.verticalCenter - initialState: settingsModel.useSelfauthenticationTestUri - onSwitched: settingsModel.useSelfauthenticationTestUri = testUriSwitch.isOn + + Accessible.name: qsTr("Test environment") + SettingsModel.translationTrigger + + initialState: SettingsModel.useSelfauthenticationTestUri + onSwitched: SettingsModel.useSelfauthenticationTestUri = testUriSwitch.isOn } } @@ -73,57 +82,61 @@ SectionPage { anchors.right: devModeSwitch.left anchors.rightMargin: Constants.component_spacing anchors.verticalCenter: devModeContainer.verticalCenter - Text { + + GText { id: devModeNameText - anchors.bottomMargin: Utils.dp(2) - font.pixelSize: Utils.dp(16) - color: Constants.secondary_text - opacity: 0.87 - text: qsTr("Developer Mode") + settingsModel.translationTrigger + anchors.bottomMargin: 2 + //: LABEL ALL_PLATFORMS + text: qsTr("Developer Mode") + SettingsModel.translationTrigger + textStyle: Style.text.normal_accent } - Text { + GText { id: devModeDateText width: parent.width anchors.top: devModeNameText.bottom - font.pixelSize: Utils.dp(14) - color: Constants.secondary_text - opacity: 0.38 - text: qsTr("Use a more tolerant mode") + settingsModel.translationTrigger - wrapMode: Text.WordWrap + //: LABEL ALL_PLATFORMS + text: qsTr("Use a more tolerant mode") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } } GSwitch { id: devModeSwitch + anchors.right: devModeContainer.right anchors.verticalCenter: devModeContainer.verticalCenter - initialState: settingsModel.developerMode - onSwitched: settingsModel.developerMode = devModeSwitch.isOn + + Accessible.name: qsTr("Developer mode") + SettingsModel.translationTrigger + + initialState: SettingsModel.developerMode + onSwitched: SettingsModel.developerMode = devModeSwitch.isOn } } - Text { - text: qsTr("Change the layout style") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - font.bold: true - color: Constants.blue + GText { + //: LABEL ALL_PLATFORMS + text: qsTr("Change the layout style") + SettingsModel.translationTrigger + textStyle: Style.text.normal_accent } GRadioButton { - text: qsTr("iOS") + settingsModel.translationTrigger + //: LABEL ALL_PLATFORMS + text: qsTr("iOS") + SettingsModel.translationTrigger checked: plugin.platformStyle === text.toLowerCase() onCheckedChanged: if (checked) { plugin.applyPlatformStyle(text.toLowerCase()) } } GRadioButton { - text: qsTr("Android") + settingsModel.translationTrigger + //: LABEL ALL_PLATFORMS + text: qsTr("Android") + SettingsModel.translationTrigger checked: plugin.platformStyle === text.toLowerCase() onCheckedChanged: if (checked) { plugin.applyPlatformStyle(text.toLowerCase()) } } GRadioButton { - text: qsTr("Tablet, Android") + settingsModel.translationTrigger + //: LABEL ALL_PLATFORMS + text: qsTr("Tablet, Android") + SettingsModel.translationTrigger checked: plugin.platformStyle === text.toLowerCase() onCheckedChanged: if (checked) { plugin.applyPlatformStyle(text.toLowerCase()) } } diff --git a/resources/qml/Governikus/EnterPasswordView/+desktop/EnterPasswordView.qml b/resources/qml/Governikus/EnterPasswordView/+desktop/EnterPasswordView.qml new file mode 100644 index 0000000..8a39b09 --- /dev/null +++ b/resources/qml/Governikus/EnterPasswordView/+desktop/EnterPasswordView.qml @@ -0,0 +1,313 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.AuthModel 1.0 +import Governikus.Type.CardReturnCode 1.0 +import Governikus.Type.NumberModel 1.0 +import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +SectionPage +{ + id: baseItem + + signal passwordEntered() + signal changePinLength() + signal requestPasswordInfo() + + property alias statusIcon: statusIcon.source + property int passwordType: NumberModel.passwordType + + Accessible.name: qsTr("Enter %1 view. You can start to enter the number.").arg(d.passwordString) + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the enter password view of the AusweisApp2.") + SettingsModel.translationTrigger + Keys.onPressed: { + if (focus && event.key >= Qt.Key_0 && event.key <= Qt.Key_9) { + numberField.append(event.key - Qt.Key_0) + numberField.forceActiveFocus() + return + } + } + + onPasswordTypeChanged: numberField.inputConfirmation = "" + onVisibleChanged: if (!visible) numberField.text = "" + + QtObject { + id: d + + readonly property string passwordString: passwordType === NumberModel.PASSWORD_PIN ? "PIN" + : passwordType === NumberModel.PASSWORD_NEW_PIN ? "PIN" + : passwordType === NumberModel.PASSWORD_CAN ? "CAN" + : passwordType === NumberModel.PASSWORD_PUK ? "PUK" + : passwordType === NumberModel.PASSWORD_REMOTE_PIN ? "Remote PIN" + : "UNKNOWN" + + function setPassword() { + if (!numberField.validInput) { + return + } + + if (passwordType === NumberModel.PASSWORD_PIN) { + NumberModel.pin = numberField.text + } + else if (passwordType === NumberModel.PASSWORD_CAN) { + NumberModel.can = numberField.text + } + else if (passwordType === NumberModel.PASSWORD_PUK) { + NumberModel.puk = numberField.text + } + else if (passwordType === NumberModel.PASSWORD_NEW_PIN) { + if (numberField.inputConfirmation === "") { + numberField.inputConfirmation = numberField.text + } else { + NumberModel.newPin = numberField.text + numberField.inputConfirmation = "" + } + } + else if (passwordType === NumberModel.PASSWORD_REMOTE_PIN) { + RemoteServiceModel.connectToRememberedServer(numberField.text) + } + + numberField.text = "" + if (numberField.inputConfirmation === "") { + baseItem.passwordEntered() + } + } + } + + GText { + visible: retryCounter.visible + anchors.horizontalCenter: retryCounter.horizontalCenter + anchors.bottom: retryCounter.top + anchors.bottomMargin: Constants.component_spacing + + //: LABEL DESKTOP_QML + text: qsTr("Attempts") + SettingsModel.translationTrigger + } + + StatusIcon { + id: retryCounter + + visible: NumberModel.retryCounter >= 0 && passwordType === NumberModel.PASSWORD_PIN + height: Style.dimens.status_icon_small + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: height + + activeFocusOnTab: true + //: LABEL DESKTOP_QML + Accessible.name: qsTr("Remaining attempts: %1").arg(NumberModel.retryCounter) + SettingsModel.translationTrigger + + text: NumberModel.retryCounter + + FocusFrame {} + } + + StatusIcon { + id: statusIcon + + height: Style.dimens.status_icon_large + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.top + anchors.verticalCenterOffset: parent.height / 4 + + busy: true + source: AuthModel.readerImage + } + + GText { + id: mainText + + visible: text !== "" + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: statusIcon.bottom + anchors.topMargin: Constants.component_spacing + + activeFocusOnTab: true + Accessible.role: Accessible.Paragraph + Accessible.name: mainText.text + + //: LABEL DESKTOP_QML %1 can be "PIN, CAN, PUK or UNKNOWN" + text: qsTr("%1-Entry").arg(d.passwordString) + SettingsModel.translationTrigger + textStyle: Style.text.header + horizontalAlignment: Text.AlignHCenter + + FocusFrame {} + } + + GText { + id: subText + + visible: text !== "" + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: Constants.text_spacing + + activeFocusOnTab: true + Accessible.role: Accessible.Paragraph + Accessible.name: subText.text + Keys.onSpacePressed: onLinkActivated(d.passwordString) + + textFormat: Text.StyledText + linkColor: Constants.white + text: { + SettingsModel.translationTrigger + + if (!numberField.confirmedInput) { + //: INFO DESKTOP_QML The changed PIN was entered wrongfully during the confirmation process. + return qsTr("The entered PIN does not match the new PIN. Please correct your input.") + } + if (passwordType === NumberModel.PASSWORD_PIN) { + if (NumberModel.requestTransportPin) { + //: INFO DESKTOP_QML The AA2 expects a PIN with 5 digit which can be entered via the physical or onscreen keyboard. + return ("%1
%2").arg(qsTr("Please enter your 5-digit Transport PIN. Use the keyboard or numpad.")).arg(qsTr("More information")) + } else { + //: INFO DESKTOP_QML The AA2 expects a PIN with 6 digit which can be entered via the physical or onscreen keyboard. + return ("%1
%2").arg(qsTr("Please enter your 6-digit PIN. Use the keyboard or numpad.")).arg(qsTr("More information")) + } + } + if (passwordType === NumberModel.PASSWORD_CAN) { + if (NumberModel.isCanAllowedMode) { + //: INFO DESKTOP_QML The user is required to enter the 6 digit CAN. + return qsTr("Please enter the 6-digit CAN you can find on the front of your ID card.") + } + //: INFO DESKTOP_QML The PIN was entered wrongfully twice, this may have happened during an earlier session of the AA2, too. The user needs to enter the CAN for additional verification. + return ("%1
%2").arg(qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.")).arg(qsTr("More information")) + } + if (passwordType === NumberModel.PASSWORD_PUK) { + //: INFO DESKTOP_QML The PUK is required to unlock the id card. + return ("%1
%2").arg(qsTr("The online identification function is blocked. Please use your personal unblocking key (PUK) to unblock your ID card.")).arg(qsTr("More information")) + } + if (passwordType === NumberModel.PASSWORD_NEW_PIN) { + if (numberField.inputConfirmation === "") { + //: INFO DESKTOP_QML A new 6-digit PIN needs to be supplied. + return qsTr("Please enter a new 6-digit PIN of your choice.") + } else { + //: INFO DESKTOP_QML The new PIN needs to be entered again for verification. + return qsTr("Please enter your new 6-digit PIN again.") + } + } + if (passwordType === NumberModel.PASSWORD_REMOTE_PIN) { + return ("%1
%2").arg(qsTr("Please start the remote service in order to use your smartphone as a card reader with AusweisApp2.")).arg(qsTr("More information")) + } + + //: INFO DESKTOP_QML Error message during PIN/CAN/PUK input procedure, the requested password type is unknown; internal error. + return qsTr("Unknown password type:") + " " + passwordType + } + textStyle: numberField.confirmedInput ? Style.text.header_secondary : Style.text.header_warning + onLinkActivated: baseItem.requestPasswordInfo() + + horizontalAlignment: Text.AlignHCenter + + FocusFrame {} + } + + Item { + anchors.top: subText.bottom + anchors.bottom: button.top + anchors.horizontalCenter: parent.horizontalCenter + + NumberField { + id: numberField + + anchors.centerIn: parent + + activeFocusOnTab: true + Accessible.name: numberField.text + + passwordLength: passwordType === NumberModel.PASSWORD_PIN && NumberModel.requestTransportPin ? 5 + : passwordType === NumberModel.PASSWORD_PUK ? 10 + : passwordType === NumberModel.PASSWORD_REMOTE_PIN ? 4 + : 6 + + onVisibleChanged: if (visible) forceActiveFocus() + + MouseArea { + anchors.fill: parent + + onClicked: numberField.forceActiveFocus() + } + + Keys.onPressed: { + if (event.key >= Qt.Key_0 && event.key <= Qt.Key_9) { + numberField.append(event.key - Qt.Key_0) + return + } + + if (event.key === Qt.Key_Backspace) { + numberField.removeLast() + return + } + + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + d.setPassword() + } + } + } + + GText { + id: changePinLengthText + + function changePinLength() { + baseItem.changePinLength() + } + + visible: passwordType === NumberModel.PASSWORD_PIN + anchors.horizontalCenter: numberField.horizontalCenter + anchors.top: numberField.bottom + anchors.topMargin: Constants.text_spacing + + activeFocusOnTab: true + Keys.onSpacePressed: changePinLengthText.changePinLength() + Accessible.name: text + + textStyle: Style.text.hint + textFormat: Text.StyledText + //: LABEL DESKTOP_QML Button, mit dem der Benutzer eine TransportPIN-Änderung starten kann. + text: "" + (NumberModel.requestTransportPin ? qsTr("Your PIN has 6 digits?") : qsTr("Your PIN has 5 digits?")) + "" + SettingsModel.translationTrigger + linkColor: Constants.white + onLinkActivated: changePinLengthText.changePinLength() + + FocusFrame {} + } + } + + NumberPad { + id: numberPad + + anchors.right: parent.right + anchors.bottom: parent.bottom + + submitEnabled: numberField.validInput + deleteEnabled: numberField.text.length > 0 + onDigitPressed: numberField.append(digit) + onDeletePressed: numberField.removeLast() + onSubmitPressed: d.setPassword() + } + + NavigationButton { + id: button + + anchors { + margins: Constants.component_spacing + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + + activeFocusOnTab: true + + buttonType: Qt.ForwardButton + enabled: numberField.validInput + onClicked: d.setPassword() + } +} diff --git a/resources/qml/Governikus/EnterPasswordView/+desktop/NumberField.qml b/resources/qml/Governikus/EnterPasswordView/+desktop/NumberField.qml new file mode 100644 index 0000000..847ebbe --- /dev/null +++ b/resources/qml/Governikus/EnterPasswordView/+desktop/NumberField.qml @@ -0,0 +1,82 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 + +Item { + id: baseItem + + property alias text: echoField.text + property alias passwordLength: echoField.maximumLength + readonly property bool validInput: echoField.acceptableInput && confirmedInput + + property string inputConfirmation + readonly property bool confirmedInput: inputConfirmation.length != text.length || inputConfirmation === text + + function append(number) { + echoField.insert(echoField.length, number) + } + function removeLast() { + echoField.remove(echoField.length-1, echoField.length) + } + + height: echoField.height + lines.anchors.topMargin + lines.height + width: lines.width + + FocusFrame { + marginFactor: 2 + } + + TextInput { + id: sample + visible: false + echoMode: echoField.echoMode + font: echoField.font + text: "0" + } + + TextInput { + id: echoField + anchors.top: parent.top + anchors.left: lines.left + anchors.leftMargin: sample.font.letterSpacing / 4 + + color: Constants.white + verticalAlignment: TextInput.AlignVCenter + echoMode: TextInput.Password + font.bold: true + font.pixelSize: ApplicationModel.scaleFactor * 50 + font.letterSpacing: ApplicationModel.scaleFactor * 40 + passwordMaskDelay: 500 + cursorVisible: false + activeFocusOnPress: false + focus: false + validator: RegExpValidator { + regExp: new RegExp("[0-9]{" + echoField.maximumLength + "}") + } + maximumLength: 6 + } + + Row { + id: lines + anchors.top: echoField.bottom + anchors.topMargin: Constants.groupbox_spacing + anchors.horizontalCenter: parent.horizontalCenter + + spacing: echoField.font.letterSpacing / 2 + + Repeater { + model: baseItem.passwordLength + delegate: Rectangle { + width: sample.contentWidth - (sample.font.letterSpacing / 2) + height: Math.max(ApplicationModel.scaleFactor * 4, 1) + color: Constants.white + } + } + } +} diff --git a/resources/qml/Governikus/EnterPasswordView/+desktop/NumberPad.qml b/resources/qml/Governikus/EnterPasswordView/+desktop/NumberPad.qml new file mode 100644 index 0000000..8698516 --- /dev/null +++ b/resources/qml/Governikus/EnterPasswordView/+desktop/NumberPad.qml @@ -0,0 +1,163 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +Item { + id: baseItem + + property alias deleteEnabled: deleteButton.enabled + property alias submitEnabled: submitButton.enabled + + signal digitPressed(string digit) + signal deletePressed() + signal submitPressed() + + QtObject { + id: d + + property var numbers + + Component.onCompleted: orderNumbers() + + function orderNumbers() { + if (SettingsModel.shuffleScreenKeyboard) { + var newNumbers = [] + for (var i = 0; i < 10; i++) { + var random = Math.floor(Math.random() * (newNumbers.length + 1)) + newNumbers.splice(random, 0, i); + } + numbers = newNumbers + } + else { + numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] + } + } + } + + NumberPadTab { + id: numberPadTab + + height: ApplicationModel.scaleFactor * 100 + + anchors.top: numPadContainer.top + anchors.right: numPadContainer.left + anchors.rightMargin: -radius + + activeFocusOnTab: true + Accessible.role: Accessible.Grouping + Accessible.name: qsTr("Numberpad to enter the password") + SettingsModel.translationTrigger + Keys.onSpacePressed: numPadContainer.toggle() + + MouseArea { + anchors.fill: parent + onClicked: numPadContainer.toggle() + } + } + + Rectangle { + id: numPadContainer + + property bool fadeIn: true + + width: grid.width + 2 * Constants.pane_padding + height: grid.height + 2 * Constants.pane_padding + + anchors.right: parent.right + anchors.rightMargin: fadeIn ? 0 : -width + anchors.bottom: parent.bottom + + color: Qt.lighter(Constants.blue, 1.15) + border.width: Math.max(1, ApplicationModel.scaleFactor * 3) + border.color: Constants.blue + + Component.onCompleted: init() + onVisibleChanged: init() + + function init() { + setVisible(SettingsModel.useScreenKeyboard) + } + + function toggle() { + if (!fadingAnimation.running) { + setVisible(!fadeIn) + } + } + + function setVisible(pVisible) { + if (pVisible) { + d.orderNumbers() + } + fadeIn = pVisible + } + + Behavior on anchors.rightMargin { + PropertyAnimation { + id: fadingAnimation + duration: 1000 + easing.type: Easing.InOutQuad + } + } + + Grid { + id: grid + + anchors.centerIn: parent + + columns: 3 + spacing: Constants.component_spacing + + Repeater { + id: numberRepeater + + model: 9 + + NumberPadButton { + activeFocusOnTab: numPadContainer.fadeIn + + text: d.numbers[index] + onClicked: baseItem.digitPressed(text) + } + } + + NumberPadButton { + id: deleteButton + + activeFocusOnTab: numPadContainer.fadeIn + + text: "C" + color: "yellow" + fontScale: 0.75 + onClicked: baseItem.deletePressed() + enabled: baseItem.deleteEnabled + } + + NumberPadButton { + id: zeroButton + + activeFocusOnTab: numPadContainer.fadeIn + + text: d.numbers[9] + onClicked: baseItem.digitPressed(text) + } + + NumberPadButton { + id: submitButton + + activeFocusOnTab: numPadContainer.fadeIn + + text: "OK" + color: "lime" + fontScale: 0.75 + onClicked: baseItem.submitPressed() + enabled: baseItem.submitEnabled + } + } + } +} diff --git a/resources/qml/Governikus/EnterPasswordView/+desktop/NumberPadButton.qml b/resources/qml/Governikus/EnterPasswordView/+desktop/NumberPadButton.qml new file mode 100644 index 0000000..74e00bb --- /dev/null +++ b/resources/qml/Governikus/EnterPasswordView/+desktop/NumberPadButton.qml @@ -0,0 +1,67 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Button { + id: control + + height: (1.5 * contentItem.font.pixelSize) / fontScale + width: height + + padding: 0 + + property real fontScale: 1 + property alias color: circle.color + + contentItem: GText { + text: control.text + textStyle: Style.text.header_inverse + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: fontScale * ApplicationModel.scaleFactor * 50 + visible: control.enabled + } + + background: Rectangle { + id: circle + + border.color: Constants.black + border.width: control.focus ? Math.max(1, ApplicationModel.scaleFactor * 2) : 0 + radius: control.height / 2 + color: Constants.white + visible: control.enabled + + Rectangle { + anchors.centerIn: parent + radius: height / 2 + color: Constants.black + opacity: 0.1 + + SequentialAnimation on height { + running: control.pressed + alwaysRunToEnd: true + + NumberAnimation { from: 0; to: background.height } + PauseAnimation { duration: 100 } + PropertyAction { value: 0 } + } + + SequentialAnimation on width { + running: control.pressed + alwaysRunToEnd: true + + NumberAnimation { from: 0; to: background.width } + PauseAnimation { duration: 100 } + PropertyAction { value: 0 } + } + } + } +} diff --git a/resources/qml/Governikus/EnterPasswordView/+desktop/NumberPadTab.qml b/resources/qml/Governikus/EnterPasswordView/+desktop/NumberPadTab.qml new file mode 100644 index 0000000..4c9f69b --- /dev/null +++ b/resources/qml/Governikus/EnterPasswordView/+desktop/NumberPadTab.qml @@ -0,0 +1,121 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Rectangle { + width: height / 2 + radius + + border.color: focus ? Constants.white : Constants.blue + border.width: Math.max(1, ApplicationModel.scaleFactor * 3) + color: Qt.lighter(Constants.blue, 1.15) + radius: height / 5 + + Timer { + id: onTimer + + property int index + + interval: 1500 + running: true + triggeredOnStart: true + + onTriggered: { + onTimer.triggeredOnStart = false + onTimer.index = Utils.getRandomInt(0, 12) + repeater.itemAt(onTimer.index).state = "on" + offTimer.restart() + } + } + + Timer { + id: offTimer + + interval: 500 + onTriggered: { + if (onTimer.index != undefined) + { + repeater.itemAt(onTimer.index).state = "" + } + + onTimer.restart() + } + } + + Rectangle { + id: reader + + width: 0.75 * height + height: parent.height - 2 * parent.radius + + anchors.left: parent.left + anchors.leftMargin: Math.max(1, ApplicationModel.scaleFactor * 4) + anchors.verticalCenter: parent.verticalCenter + + color: "skyblue" + radius: height * 0.1 + + Grid { + id: pinGrid + + anchors.fill: parent + + columns: 3 + padding: parent.width * 0.1 + spacing: parent.width * 0.1 + + Repeater { + id: repeater + + model: 12 + + Item { + id: pinButton + + width: (reader.width - 4 * pinGrid.spacing) / 3 + height: width + + Rectangle { + id: pinButtonCircle + + width: pinButton.width + height: width + + anchors.centerIn: parent + + state: parent.state + radius: width * 0.5 + color: index == 9 ? "yellow" : index == 11 ? "lime" : Constants.white + + Behavior on color { + ColorAnimation { + duration: 250 + } + } + + Behavior on width { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutElastic + } + } + + states: [ + State { + name: "on" + + PropertyChanges {target: pinButtonCircle; color: Constants.blue} + PropertyChanges {target: pinButtonCircle; width: pinButton.width + pinButton.width * 0.3} + } + ] + } + } + } + } + } +} diff --git a/resources/qml/Governikus/EnterPasswordView/+desktop/PasswordInfoView.qml b/resources/qml/Governikus/EnterPasswordView/+desktop/PasswordInfoView.qml new file mode 100644 index 0000000..4572ced --- /dev/null +++ b/resources/qml/Governikus/EnterPasswordView/+desktop/PasswordInfoView.qml @@ -0,0 +1,146 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.NumberModel 1.0 + + +SectionPage { + id: root + + signal close() + + property int passwordType: NumberModel.passwordType + + Accessible.name: qsTr("Password information") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the password information section of the AusweisApp2.") + SettingsModel.translationTrigger + Keys.onReturnPressed: close() + Keys.onEnterPressed: close() + Keys.onEscapePressed: close() + + titleBarAction: TitleBarAction { + rootEnabled: false + showSettings: false + text: d.passwordString + } + + QtObject { + id: d + + readonly property string passwordString: passwordType === NumberModel.PASSWORD_PIN ? "PIN" + : passwordType === NumberModel.PASSWORD_CAN ? "CAN" + : passwordType === NumberModel.PASSWORD_PUK ? "PUK" + : passwordType === NumberModel.PASSWORD_REMOTE_PIN ? "Remote PIN" + : "UNKNOWN" + } + + GText { + id: headline + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Constants.pane_padding + + activeFocusOnTab: true + Accessible.name: headline.text + + wrapMode: Text.WordWrap + //: %1 can be "PIN, CAN, PUK or UNKNOWN" + text: qsTr("%1 information").arg(d.passwordString) + SettingsModel.translationTrigger + textStyle: Style.text.header + FocusFrame { + dynamic: false + } + } + + GText { + id: infoText + + anchors.top: headline.bottom + anchors.left: parent.left + anchors.right: infoImage.left + anchors.margins: Constants.pane_padding + + activeFocusOnTab: true + Accessible.name: infoText.text + + text: { + SettingsModel.translationTrigger + + if (passwordType === NumberModel.PASSWORD_CAN) { + //: INFO DESKTOP_QML Description text of CAN + return qsTr("The card access number (CAN) is only required if you have already entered the PIN incorrectly twice. In order to prevent a third incorrect entry and thus the blocking of the ID card without your consent, the CAN is also requested at this point. The CAN is a six-digit number that can be found on the front of the ID card. It is located at the bottom right next to the validity date (marked in red).") + } + if (passwordType === NumberModel.PASSWORD_PUK) { + //: INFO DESKTOP_QML Description text of PUK + return qsTr("The personal unblocking key (PUK) is only required if you entered the the wrong PIN three times. The online eID function is blocked at this time. The PUK is a ten-digit number you received with the letter sent to you by your competent authority (marked in red). Please note that you can only use the PUK to unblock the PIN entry. If you have forgotten your PIN, you can have a new PIN set at your competent authority.") + } + if (passwordType === NumberModel.PASSWORD_REMOTE_PIN) { + //: INFO DESKTOP_QML Description text of SaC pairing + return qsTr("You may use your smartphone as a card reader with AusweisApp2. The smartphone needs to feature a supported NFC chipset and both devices, your smartphone and this machine, need to be connected to the same WiFi network.

Please make sure that the remote service is running on your smartphone. Start the pairing process by clicking the \"Start pairing\" button and enter the shown 4-digit PIN.") + } + + //: INFO DESKTOP_QML Description text of PIN + return qsTr("The personal identification number (PIN) is required for every use of the online eID function. You can change it anytime and indefinitely if you know your valid PIN. For your 6-digit PIN choose a combination of numbers, that is not easy to guess, neither \"123456\" nor your birth date, or any other numbers printed on the ID card. If you are no longer aware of your valid PIN, you will need to contact the authority responsible for issuing your identification document to renew your PIN.\n\nWhen changing the PIN for the first time, please use your 5-digit transport PIN. You will find the transport PIN in the letter you received from the authority responsible for issuing your identification document (marked in red) after you have applied for your identity card.\n\nPlease note that you can not use the online eID function with your 5-digit transport PIN. A change to a 6-digit PIN is mandatory.") + } + textStyle: Style.text.header + + FocusFrame { + dynamic: false + } + } + + Image { + id: infoImage + + sourceSize.width: (passwordType === NumberModel.PASSWORD_CAN ? 800 : 560) * ApplicationModel.scaleFactor + anchors.top: headline.bottom + anchors.right: parent.right + anchors.margins: Constants.pane_padding + + verticalAlignment: Image.AlignTop + fillMode: Image.PreserveAspectFit + source: passwordType === NumberModel.PASSWORD_CAN ? "qrc:///images/desktop/id_card.png" + : passwordType === NumberModel.PASSWORD_PUK ? "qrc:///images/desktop/pin-letter-page2.png" + : passwordType === NumberModel.PASSWORD_REMOTE_PIN ? "qrc:///images/phone_to_pc.svg" + : "qrc:///images/desktop/pin-letter-page1.png" + + Rectangle { + visible: passwordType === NumberModel.PASSWORD_CAN + height: 50 * ApplicationModel.scaleFactor + width: 160 * ApplicationModel.scaleFactor + anchors.top: parent.top + anchors.topMargin: 333 * ApplicationModel.scaleFactor + anchors.left: parent.left + anchors.leftMargin: 598 * ApplicationModel.scaleFactor + + color: Style.color.transparent + border.color: Constants.red + border.width: Math.max(1, 6 * ApplicationModel.scaleFactor) + } + } + + NavigationButton { + id: button + + anchors { + margins: Constants.component_spacing + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + + buttonType: Qt.BackButton + onClicked: root.close() + } +} diff --git a/resources/qml/Governikus/EnterPinView/EnterPinView.qml b/resources/qml/Governikus/EnterPasswordView/+mobile/EnterPasswordView.qml similarity index 73% rename from resources/qml/Governikus/EnterPinView/EnterPinView.qml rename to resources/qml/Governikus/EnterPasswordView/+mobile/EnterPasswordView.qml index 1dde17b..e2a844e 100644 --- a/resources/qml/Governikus/EnterPinView/EnterPinView.qml +++ b/resources/qml/Governikus/EnterPasswordView/+mobile/EnterPasswordView.qml @@ -1,8 +1,14 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.1 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.RemoteServiceModel 1.0 import Governikus.Type.NumberModel 1.0 @@ -11,7 +17,7 @@ SectionPage { id: baseItem property alias enableTransportPinLink: transportPinLink.enableTransportPinLink - signal pinEntered() + signal passwordEntered() signal changePinLength() onVisibleChanged: { @@ -33,34 +39,32 @@ SectionPage Item {/*spacer*/ Layout.fillWidth: true; height: parent.height} Image { Layout.alignment: Qt.AlignVCenter - width: Utils.dp(58) + width: 58 height: width // because RowLayout uses implicitHeight that is based on sourceSize we have to explicitly set the sourceSize - sourceSize.height: baseItem.state === "REMOTE_PIN" ? Utils.dp(58) : 143 + sourceSize.height: baseItem.state === "REMOTE_PIN" ? 58 : 143 Layout.maximumWidth: width source: baseItem.state === "REMOTE_PIN" ? "qrc:///images/icon_remote.svg" : "qrc:///images/NFCPhoneCard.png" fillMode: Image.PreserveAspectFit } - Text { + GText { Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true Layout.maximumWidth: Math.ceil(implicitWidth) - wrapMode: Text.WordWrap - font.pixelSize: text.length > 150 && !Constants.is_tablet? Utils.dp(15) : Constants.header_font_size - font.bold: true - color: { + textStyle: { if (!pinField.confirmedInput || !!NumberModel.inputError || baseItem.state === "CAN" || baseItem.state === "PUK") { - Constants.red + Style.text.normal_warning } else { - Constants.blue + Style.text.normal_accent } } text: { - settingsModel.translationTrigger + SettingsModel.translationTrigger if (!pinField.confirmedInput) { + //: INFO ANDROID IOS The changed PIN was entered wrongfully during confirmation. return qsTr("The entered PIN does not match the new PIN. Please correct your input.") } if (!!NumberModel.inputError) { @@ -68,26 +72,34 @@ SectionPage } if (baseItem.state === "CAN") { if (NumberModel.isCanAllowedMode) { + //: INFO ANDROID IOS The CAN needs to be entered in CAN-allowed mode, hint where the CAN can be found. return qsTr("Please enter the six-digit card access number. You can find the card access number on the front of the ID card.") } + //: INFO ANDROID IOS The wrong PIN was entered twice, the third attempt requires the CAN for additional verification, hint where the CAN is found. return qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.") } if (baseItem.state === "PUK") { + //: INFO ANDROID IOS The PUK is required to unlock the id card since the wrong PIN entered three times. return qsTr("You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking.") } if (baseItem.state === "PIN_NEW") { + //: INFO ANDROID IOS A new 6-digit PIN needs to be supplied. return qsTr("Please enter a new 6-digit PIN of your choice.") } if (baseItem.state === "PIN_NEW_AGAIN") { + //: INFO ANDROID IOS The new PIN needs to be confirmed. return qsTr("Please enter your new 6-digit PIN again.") } if (baseItem.state === "PIN" && NumberModel.requestTransportPin) { + //: INFO ANDROID IOS The transport PIN is required by AA2, it needs to be change to an actual PIN. return qsTr("Please enter your transport PIN.") } if (baseItem.state === "REMOTE_PIN") { + //: INFO ANDROID IOS The pairing code for the smartphone is required. return qsTr("Enter the pairing code shown on your other device to use it as a card reader.") } - return qsTr("Please enter your personal PIN.") + //: LABEL ANDROID IOS + return qsTr("Please enter your PIN.") } } Item {/*spacer*/ Layout.fillWidth: true; height: parent.height} @@ -95,7 +107,7 @@ SectionPage Item {/*spacer*/ Layout.fillHeight: true; width: parent.width } - PinField { + NumberField { id: pinField Layout.alignment: Qt.AlignHCenter passwordLength: baseItem.state === "REMOTE_PIN" ? 4 @@ -106,29 +118,29 @@ SectionPage Layout.preferredWidth: width } - Text { + GText { id: transportPinLink property alias enableTransportPinLink: myMouse.enabled visible: baseItem.state === "PIN" && enableTransportPinLink - text: (NumberModel.requestTransportPin ? qsTr("Your PIN has 6 digits?") : qsTr("Your PIN has 5 digits?")) + settingsModel.translationTrigger + //: LABEL ANDROID IOS Button, mit dem der Benutzer eine TransportPIN-Änderung starten kann. + text: (NumberModel.requestTransportPin ? qsTr("Your PIN has 6 digits?") : qsTr("Your PIN has 5 digits?")) + SettingsModel.translationTrigger Layout.alignment: Qt.AlignHCenter - font.pixelSize: Constants.small_font_size - color: Constants.blue + textStyle: Style.text.hint_accent font.underline: true MouseArea { id: myMouse enabled: true anchors.fill: parent - anchors.margins: -Utils.dp(12) + anchors.margins: -12 onClicked: baseItem.changePinLength() } } Item {/*spacer*/ height: Constants.component_spacing; width: parent.width } - PinPad { + NumberPad { state: baseItem.state Layout.alignment: Qt.AlignHCenter Layout.preferredWidth: width @@ -142,7 +154,7 @@ SectionPage switch(baseItem.state) { case "PIN": NumberModel.pin = pinField.text - baseItem.pinEntered() + baseItem.passwordEntered() break case "PIN_NEW": pinField.inputConfirmation = pinField.text @@ -151,19 +163,19 @@ SectionPage break case "PIN_NEW_AGAIN": NumberModel.newPin = pinField.text - baseItem.pinEntered() + baseItem.passwordEntered() break case "CAN": NumberModel.can = pinField.text - baseItem.pinEntered() + baseItem.passwordEntered() break case "PUK": NumberModel.puk = pinField.text - baseItem.pinEntered() + baseItem.passwordEntered() break case "REMOTE_PIN": RemoteServiceModel.connectToRememberedServer(pinField.text) - baseItem.pinEntered() + baseItem.passwordEntered() break } } diff --git a/resources/qml/Governikus/EnterPinView/PinField.qml b/resources/qml/Governikus/EnterPasswordView/+mobile/NumberField.qml similarity index 87% rename from resources/qml/Governikus/EnterPinView/PinField.qml rename to resources/qml/Governikus/EnterPasswordView/+mobile/NumberField.qml index 7f34f8e..4d35168 100644 --- a/resources/qml/Governikus/EnterPinView/PinField.qml +++ b/resources/qml/Governikus/EnterPasswordView/+mobile/NumberField.qml @@ -1,6 +1,11 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 Item { id: baseItem @@ -24,11 +29,11 @@ Item { TextInput { id: echoField - color: Constants.secondary_text + color: Style.color.primary_text verticalAlignment: TextInput.AlignVCenter echoMode: TextInput.Password - font.pixelSize: Utils.dp(18) - font.letterSpacing: Utils.dp(10) + font.pixelSize: 18 + font.letterSpacing: 10 passwordMaskDelay: 500 cursorVisible: false activeFocusOnPress: false diff --git a/resources/qml/Governikus/EnterPasswordView/+mobile/NumberPad.qml b/resources/qml/Governikus/EnterPasswordView/+mobile/NumberPad.qml new file mode 100644 index 0000000..0346901 --- /dev/null +++ b/resources/qml/Governikus/EnterPasswordView/+mobile/NumberPad.qml @@ -0,0 +1,70 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.1 + +import Governikus.Global 1.0 +import Governikus.Type.SettingsModel 1.0 + +GridLayout { + id: baseItem + + property bool deleteEnabled: true + property bool submitEnabled: true + + signal digitPressed(string digit) + signal deletePressed() + signal submitPressed() + + columns: 3 + columnSpacing: 10 + rowSpacing: columnSpacing + width: 250 + height: width + Layout.fillHeight: false + Layout.fillWidth: false + + NumberPadButton { text: "1"; onClicked: baseItem.digitPressed("1"); Layout.fillHeight: true; Layout.fillWidth: true } + NumberPadButton { text: "2"; onClicked: baseItem.digitPressed("2"); Layout.fillHeight: true; Layout.fillWidth: true } + NumberPadButton { text: "3"; onClicked: baseItem.digitPressed("3"); Layout.fillHeight: true; Layout.fillWidth: true } + NumberPadButton { text: "4"; onClicked: baseItem.digitPressed("4"); Layout.fillHeight: true; Layout.fillWidth: true } + NumberPadButton { text: "5"; onClicked: baseItem.digitPressed("5"); Layout.fillHeight: true; Layout.fillWidth: true } + NumberPadButton { text: "6"; onClicked: baseItem.digitPressed("6"); Layout.fillHeight: true; Layout.fillWidth: true } + NumberPadButton { text: "7"; onClicked: baseItem.digitPressed("7"); Layout.fillHeight: true; Layout.fillWidth: true } + NumberPadButton { text: "8"; onClicked: baseItem.digitPressed("8"); Layout.fillHeight: true; Layout.fillWidth: true } + NumberPadButton { text: "9"; onClicked: baseItem.digitPressed("9"); Layout.fillHeight: true; Layout.fillWidth: true } + NumberPadButton { + visible: baseItem.deleteEnabled + Layout.fillHeight: true + Layout.fillWidth: true + + Accessible.name: qsTr("Delete last digit") + SettingsModel.translationTrigger + + source: "qrc:///images/delete.svg" + + onClicked: baseItem.deletePressed() + } + NumberPadButton { + Layout.fillHeight: true + Layout.fillWidth: true + Layout.column: 1 + Layout.row: 3 + + text: "0" + + onClicked: baseItem.digitPressed("0") + } + NumberPadButton { + visible: baseItem.submitEnabled + Layout.fillHeight: true + Layout.fillWidth: true + + Accessible.name: qsTr("Submit") + SettingsModel.translationTrigger + + source: "qrc:///images/submit.svg" + + onClicked: baseItem.submitPressed() + } +} diff --git a/resources/qml/Governikus/EnterPinView/PinPadButton.qml b/resources/qml/Governikus/EnterPasswordView/+mobile/NumberPadButton.qml similarity index 62% rename from resources/qml/Governikus/EnterPinView/PinPadButton.qml rename to resources/qml/Governikus/EnterPasswordView/+mobile/NumberPadButton.qml index 0e62880..90da38c 100644 --- a/resources/qml/Governikus/EnterPinView/PinPadButton.qml +++ b/resources/qml/Governikus/EnterPasswordView/+mobile/NumberPadButton.qml @@ -1,25 +1,37 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 import Governikus.Global 1.0 +import Governikus.Style 1.0 MouseArea { id: baseItem + property alias text: textItem.text property alias source: imageItem.source - Text { + Accessible.role: Accessible.Button + Accessible.name: text + Accessible.onPressAction: if (Qt.platform.os === "ios" && enabled) clicked(null) + + GText { id: textItem + anchors.centerIn: parent - font.pixelSize: Utils.dp(24) - wrapMode: Text.WordWrap - color: Constants.secondary_text + + Accessible.ignored: true + + textStyle: Style.text.title } Image { id: imageItem anchors.centerIn: parent - height:Utils.dp(36) + height: 36 width: height fillMode: Image.PreserveAspectFit } @@ -29,14 +41,14 @@ MouseArea { anchors.centerIn: parent height: width radius: width /2 - color: "#000000" + color: Constants.black opacity: 0.1 SequentialAnimation on width { running: baseItem.pressed alwaysRunToEnd: true NumberAnimation { from: 0; to: Math.SQRT2 * baseItem.width } PauseAnimation { duration: 100 } - PropertyAction { value: 0} + PropertyAction { value: 0 } } } } diff --git a/resources/qml/Governikus/EnterPasswordView/qmldir b/resources/qml/Governikus/EnterPasswordView/qmldir new file mode 100644 index 0000000..5ff2ee6 --- /dev/null +++ b/resources/qml/Governikus/EnterPasswordView/qmldir @@ -0,0 +1,9 @@ +module EnterPasswordView + +internal NumberField NumberField.qml +internal NumberPad NumberPad.qml +internal NumberPadButton NumberPadButton.qml +internal NumberPadTab NumberPadTab.qml + +PasswordInfoView 1.0 PasswordInfoView.qml +EnterPasswordView 1.0 EnterPasswordView.qml diff --git a/resources/qml/Governikus/EnterPinView/PinPad.qml b/resources/qml/Governikus/EnterPinView/PinPad.qml deleted file mode 100644 index fccc4c6..0000000 --- a/resources/qml/Governikus/EnterPinView/PinPad.qml +++ /dev/null @@ -1,46 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Layouts 1.1 - -import Governikus.Global 1.0 - -GridLayout { - id: baseItem - - property bool deleteEnabled: true - property bool submitEnabled: true - - signal digitPressed(string digit) - signal deletePressed() - signal submitPressed() - - columns: 3 - columnSpacing: Utils.dp(10) - rowSpacing: columnSpacing - width: Utils.dp(250) - height: width - Layout.fillHeight: false - Layout.fillWidth: false - - PinPadButton { text: "1"; onClicked: baseItem.digitPressed("1"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { text: "2"; onClicked: baseItem.digitPressed("2"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { text: "3"; onClicked: baseItem.digitPressed("3"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { text: "4"; onClicked: baseItem.digitPressed("4"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { text: "5"; onClicked: baseItem.digitPressed("5"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { text: "6"; onClicked: baseItem.digitPressed("6"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { text: "7"; onClicked: baseItem.digitPressed("7"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { text: "8"; onClicked: baseItem.digitPressed("8"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { text: "9"; onClicked: baseItem.digitPressed("9"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { - source: baseItem.deleteEnabled ? "qrc:///images/delete.svg" : "" - onClicked: baseItem.deletePressed() - enabled: baseItem.deleteEnabled - Layout.fillHeight: true; Layout.fillWidth: true - } - PinPadButton { text: "0"; onClicked: baseItem.digitPressed("0"); Layout.fillHeight: true; Layout.fillWidth: true } - PinPadButton { - source: baseItem.submitEnabled ? "qrc:///images/submit.svg" : "" - onClicked: baseItem.submitPressed() - enabled: baseItem.submitEnabled - Layout.fillHeight: true; Layout.fillWidth: true - } -} diff --git a/resources/qml/Governikus/EnterPinView/qmldir b/resources/qml/Governikus/EnterPinView/qmldir deleted file mode 100644 index f333009..0000000 --- a/resources/qml/Governikus/EnterPinView/qmldir +++ /dev/null @@ -1,7 +0,0 @@ -module EnterPinView - -internal PinField PinField.qml -internal PinPadButton PinPadButton.qml -internal PinPad PinPad.qml - -EnterPinView 1.0 EnterPinView.qml diff --git a/resources/qml/Governikus/FeedbackView/+desktop/LogView.qml b/resources/qml/Governikus/FeedbackView/+desktop/LogView.qml new file mode 100644 index 0000000..e566591 --- /dev/null +++ b/resources/qml/Governikus/FeedbackView/+desktop/LogView.qml @@ -0,0 +1,179 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.LogModel 1.0 + + +SectionPage +{ + id: root + + Accessible.name: qsTr("Logfile viewer") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the logfile viewer of the AusweisApp2.") + SettingsModel.translationTrigger + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Application log") + SettingsModel.translationTrigger + } + + TabbedPane { + id: tabbedPane + + anchors.fill: parent + anchors.margins: Constants.pane_padding + + sectionsModel: LogModel.logFiles + onCurrentIndexChanged: LogModel.setLogfile(currentIndex) + + contentDelegate: logSectionDelegate + + footerItem: Item { + height: buttonLayout.implicitHeight + + ColumnLayout { + id: buttonLayout + + anchors.fill: parent + anchors.rightMargin: Constants.groupbox_spacing + spacing: Constants.groupbox_spacing + + GButton { + id: saveLog + + Layout.fillWidth: true + activeFocusOnTab: true + + icon.source: "qrc:///images/icon_save.svg" + //: LABEL DESKTOP_QML + text: qsTr("Save logfile") + SettingsModel.translationTrigger + enabled: tabbedPane.sectionsModel.length > 0 + onClicked: { + var creationDate = LogModel.getCurrentLogfileDate().toLocaleString(Qt.locale(), "yyyy-MM-dd_HH-mm") + var filenameSuggestion = "%1.%2.log".arg(Qt.application.name).arg(creationDate) + appWindow.openSaveFileDialog(LogModel.saveCurrentLogfile, filenameSuggestion, "log") + } + } + + GButton { + id: removeLog + + Layout.fillWidth: true + activeFocusOnTab: true + + icon.source: "qrc:///images/trash_icon_white.svg" + //: LABEL DESKTOP_QML + text: qsTr("Delete logfile") + SettingsModel.translationTrigger + enabled: tabbedPane.currentIndex > 0 + onClicked: { + confirmationPopup.deleteAll = false + confirmationPopup.open() + } + } + + GButton { + id: removeAllLogs + + Layout.fillWidth: true + activeFocusOnTab: true + + icon.source: "qrc:///images/trash_icon_all.svg" + //: LABEL DESKTOP_QML + text: qsTr("Delete old logfiles") + SettingsModel.translationTrigger + enabled: tabbedPane.sectionsModel.length > 1 + onClicked: { + confirmationPopup.deleteAll = true + confirmationPopup.open() + } + } + } + } + } + + Component { + id: logSectionDelegate + + Item { + height: tabbedPane.availableHeight + width: parent.width + + ListView { + id: logView + + anchors.fill: parent + + activeFocusOnTab: true + + displayMarginBeginning: Constants.pane_padding + displayMarginEnd: Constants.pane_padding + boundsBehavior: Flickable.StopAtBounds + model: LogModel + delegate: GText { + width: parent.width - 2 * Constants.pane_padding + bottomPadding: index % 2 ? Constants.groupbox_spacing : 0 + + textStyle: index % 2 ? Style.text.hint_inverse : Style.text.hint_secondary_inverse + text: display + } + ScrollBar.vertical: ScrollBar { + id: verticalScrollbar + + width: ApplicationModel.scaleFactor * 18 + anchors.horizontalCenter: parent.right + anchors.horizontalCenterOffset: Constants.pane_padding / 2 + + policy: ScrollBar.AlwaysOn + minimumSize: Style.dimens.minimumScrollBarSize + stepSize: logView.height / logView.contentHeight + } + + Connections { + target: LogModel + onFireNewLogMsg: if (logView.atYEnd) logView.positionViewAtEnd() + } + } + + FocusFrame { + scope: logView + framee: logView + dynamic: false + border.color: Constants.black + } + } + } + + Keys.onPressed: { + if (event.key === Qt.Key_PageDown) + verticalScrollbar.increase() + else if (event.key === Qt.Key_PageUp) + verticalScrollbar.decrease() + else if (event.key === Qt.Key_End) + logView.positionViewAtEnd() + else if (event.key === Qt.Key_Home) + logView.positionViewAtBeginning() + } + + ConfirmationPopup { + id: confirmationPopup + + property bool deleteAll: true + + //: LABEL DESKTOP_QML + title: (deleteAll ? qsTr("Delete old logfiles") : qsTr("Delete selected logfile")) + SettingsModel.translationTrigger + //: INFO DESKTOP_QML The current/all logfile(s) are about to be removed, user confirmation required. + text: (deleteAll ? qsTr("Please confirm that you want to delete your old logfiles.") + : qsTr("Please confirm that you want to delete the logfile.") + ) + SettingsModel.translationTrigger + onConfirmed: deleteAll ? LogModel.removeOtherLogfiles() : LogModel.removeCurrentLogfile() + } +} diff --git a/resources/qml/Governikus/FeedbackView/+mobile/+android/StoreFeedbackPopup.qml b/resources/qml/Governikus/FeedbackView/+mobile/+android/StoreFeedbackPopup.qml new file mode 100644 index 0000000..ffac994 --- /dev/null +++ b/resources/qml/Governikus/FeedbackView/+mobile/+android/StoreFeedbackPopup.qml @@ -0,0 +1,24 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + +ConfirmationPopup { + //: INFO ANDROID Header of the app rating popup. + title: qsTr("Would you like to rate this app?") + SettingsModel.translationTrigger + //: INFO ANDROID Content of the app rating popup. + text: qsTr("We would be very grateful if you could leave a rating on the Google Play Store!") + SettingsModel.translationTrigger + //: LABEL ANDROID + cancelButtonText: qsTr("No, thanks") + SettingsModel.translationTrigger + //: LABEL ANDROID + okButtonText: qsTr("Rate") + SettingsModel.translationTrigger + + onConfirmed: Qt.openUrlExternally("market://details?id=" + ApplicationModel.packageName) +} diff --git a/resources/qml/Governikus/FeedbackView/+mobile/+ios/StoreFeedbackPopup.qml b/resources/qml/Governikus/FeedbackView/+mobile/+ios/StoreFeedbackPopup.qml new file mode 100644 index 0000000..7114861 --- /dev/null +++ b/resources/qml/Governikus/FeedbackView/+mobile/+ios/StoreFeedbackPopup.qml @@ -0,0 +1,13 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Type.ApplicationModel 1.0 + +Item { + function open() { + ApplicationModel.showAppStoreRatingDialog(); + } +} \ No newline at end of file diff --git a/resources/qml/Governikus/FeedbackView/+mobile/Feedback.qml b/resources/qml/Governikus/FeedbackView/+mobile/Feedback.qml index 9fc14b8..f443eca 100644 --- a/resources/qml/Governikus/FeedbackView/+mobile/Feedback.qml +++ b/resources/qml/Governikus/FeedbackView/+mobile/Feedback.qml @@ -1,24 +1,28 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 -import Governikus.Type.LogModel 1.0 - import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.LogModel 1.0 SectionPage { id: root - headerTitleBarAction: TitleBarAction { text: qsTr("Help & Feedback") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID IOS + title: qsTr("Help & Feedback") + SettingsModel.translationTrigger Component { id: lineSeparator - Rectangle { - height: 1 - color: Constants.grey + GSeparator { } } Component { @@ -30,18 +34,13 @@ SectionPage { anchors.left: parent.left anchors.right: parent.right spacing: Constants.component_spacing - Text { + GText { width: parent.width - font.pixelSize: Utils.dp(18) - color: Constants.blue - wrapMode: Text.WordWrap text: titleText + textStyle: Style.text.normal_accent } - Text { - color: Constants.secondary_text + GText { width: parent.width - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap text: descriptionText } } @@ -51,7 +50,7 @@ SectionPage { } } } - Log { + LogView { id: logPage visible: false } @@ -69,40 +68,52 @@ SectionPage { Pane { Loader { - readonly property string titleText: qsTr("FAQ") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("Do you have questions how to use AusweisApp2?") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string titleText: qsTr("FAQ") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string descriptionText: qsTr("Do you have questions how to use AusweisApp2?") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS function onClickFunction() { Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/frequently-asked-questions/")) } width: parent.width sourceComponent: subMenu } Loader { width: parent.width; sourceComponent: lineSeparator } Loader { - readonly property string titleText: qsTr("Support") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("You need further help?") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string titleText: qsTr("Support") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string descriptionText: qsTr("You need further help?") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS function onClickFunction() { Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/support/")) } width: parent.width sourceComponent: subMenu } Loader { width: parent.width; sourceComponent: lineSeparator } Loader { - readonly property string titleText: qsTr("Rate AusweisApp2") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("Please rate us in the Google Play Store.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string titleText: qsTr("Rate AusweisApp2") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string descriptionText: qsTr("Please rate us in the Google Play Store.") + SettingsModel.translationTrigger function onClickFunction() { Qt.openUrlExternally("market://details?id=" + ApplicationModel.packageName) } width: parent.width sourceComponent: subMenu } Loader { width: parent.width; sourceComponent: lineSeparator } Loader { - readonly property string titleText: qsTr("Report error") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("You found a bug? Please tell us, so we can fix it.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string titleText: qsTr("Report error") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string descriptionText: qsTr("You found a bug? Please tell us, so we can fix it.") + SettingsModel.translationTrigger function onClickFunction() { LogModel.mailLog() } width: parent.width sourceComponent: subMenu } Loader { width: parent.width; sourceComponent: lineSeparator } Loader { - readonly property string titleText: qsTr("Show log") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("You can view the logs of the AusweisApp2 here.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string titleText: qsTr("Show log") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string descriptionText: qsTr("You can view the logs of the AusweisApp2 here.") + SettingsModel.translationTrigger function onClickFunction() { firePush(logPage) } width: parent.width sourceComponent: subMenu diff --git a/resources/qml/Governikus/FeedbackView/+mobile/Log.qml b/resources/qml/Governikus/FeedbackView/+mobile/Log.qml deleted file mode 100644 index 94080ce..0000000 --- a/resources/qml/Governikus/FeedbackView/+mobile/Log.qml +++ /dev/null @@ -1,143 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.View 1.0 -import Governikus.Type.LogModel 1.0 - - -SectionPage -{ - id: root - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Log") + settingsModel.translationTrigger; font.bold: true } - rightTitleBarAction: LogTitleBarControls { - allowRemove: comboBox.currentIndex > 0 - - onShare: { - LogModel.shareLog() - } - - onRemove: { - confirmationPopup.deleteAll = false - confirmationPopup.open() - } - - onRemoveAll: { - confirmationPopup.deleteAll = true - confirmationPopup.open() - } - } - - Item { - id: logSelector - height: comboBox.height - width: parent.width - 2 * Constants.pane_padding - anchors.top: parent.top - anchors.topMargin: Constants.pane_padding - anchors.horizontalCenter: parent.horizontalCenter - - Text { - id: comboText - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - - color: Constants.black - font.pixelSize: Constants.normal_font_size - text: qsTr("Log:") + settingsModel.translationTrigger - } - - GComboBox { - id: comboBox - width: parent.width - comboText.width - Constants.component_spacing - anchors.right: parent.right - model: LogModel.logFiles - onActivated: LogModel.setLogfile(comboBox.currentIndex) - } - } - - Pane { - id: pane - anchors.top: logSelector.bottom - anchors.bottom: parent.bottom - anchors.margins: Constants.pane_padding - - Item { - height: pane.height - 2 * Constants.pane_padding - width: pane.width - 2 * Constants.pane_padding - - ListView { - id: logView - clip: true - model: LogModel - anchors.fill: parent - maximumFlickVelocity: Constants.scrolling_speed - - delegate: Text { - width: pane.width - 2* Constants.pane_padding - wrapMode: Text.Wrap - font.pixelSize: Constants.small_font_size - font.bold: !(index % 2) - text: model.display - textFormat: Text.PlainText - } - - onAtYEndChanged: LogModel.autoFlick = atYEnd - - onContentYChanged: { - if (visibleArea.yPosition < 0.1) { - LogModel.moveView(-20) - return - } - - if (visibleArea.yPosition + visibleArea.heightRatio > 0.9) { - LogModel.moveView(20) - } - } - - Connections { - target: LogModel - onFireNewLogMsg: logView.positionViewAtEnd() - } - } - - ScrollBar { - id: mainScroll - width: Utils.dp(8) - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.right - anchors.horizontalCenterOffset: Constants.pane_padding / 2 - - active: true - orientation: Qt.Vertical - minimumSize: 2 * (width / height) - size: LogModel.heightRatio * logView.visibleArea.heightRatio - position: LogModel.yPosition + LogModel.heightRatio * logView.visibleArea.yPosition - onPressedChanged: { - if (pressed) { - logView.cancelFlick() - } else { - active = true - LogModel.yPosition = position - } - } - } - } - } - - ConfirmationPopup { - id: confirmationPopup - - property bool deleteAll: true - - baseItem: root - title: (deleteAll ? qsTr("Delete all") : qsTr("Delete")) + settingsModel.translationTrigger - text: (deleteAll ? qsTr("Please confirm that you want to delete your old logfiles.") - : qsTr("Please confirm that you want to delete the logfile.") - ) + settingsModel.translationTrigger - confirmText: qsTr("Delete") + settingsModel.translationTrigger - onConfirmed: deleteAll ? LogModel.removeOtherLogfiles() : LogModel.removeCurrentLogfile() - } -} diff --git a/resources/qml/Governikus/FeedbackView/+mobile/LogTitleBarControls.qml b/resources/qml/Governikus/FeedbackView/+mobile/LogTitleBarControls.qml index 3aabb00..b07b766 100644 --- a/resources/qml/Governikus/FeedbackView/+mobile/LogTitleBarControls.qml +++ b/resources/qml/Governikus/FeedbackView/+mobile/LogTitleBarControls.qml @@ -1,57 +1,91 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 Row { id: logControls - signal share() + signal share(point popupPosition) signal remove() signal removeAll() readonly property int contentWidth: width property alias allowRemove: removeButton.visible + property alias allowRemoveAll: removeAllButton.visible - spacing: Utils.dp(18) + spacing: 18 anchors.verticalCenter: parent ? parent.verticalCenter : undefined Image { - height: Constants.titlebar_font_size * 1.5 + height: Style.dimens.small_icon_size width: height + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Share logfile") + SettingsModel.translationTrigger + Accessible.onPressAction: if (Qt.platform.os === "ios") mouseAreaShare.clicked(null) + fillMode: Image.PreserveAspectFit source: "qrc:///images/share.svg" MouseArea { + id: mouseAreaShare + anchors.fill: parent - anchors.margins: -Utils.dp(8) - onClicked: logControls.share() + anchors.margins: -8 + + onClicked: logControls.share(mapToGlobal(width / 2, height)) } } Image { id: removeButton - height: Constants.titlebar_font_size * 1.5 + + height: Style.dimens.small_icon_size width: height + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Delete logfile") + SettingsModel.translationTrigger + Accessible.onPressAction: if (Qt.platform.os === "ios") mouseAreaRemove.clicked(null) + fillMode: Image.PreserveAspectFit source: "qrc:///images/trash_icon_white.svg" MouseArea { + id: mouseAreaRemove + anchors.fill: parent - anchors.margins: -Utils.dp(8) + anchors.margins: -8 + onClicked: logControls.remove() } } Image { - height: Constants.titlebar_font_size * 1.5 + id: removeAllButton + + height: Style.dimens.small_icon_size width: height + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Delete all logfiles") + SettingsModel.translationTrigger + Accessible.onPressAction: if (Qt.platform.os === "ios") mouseAreaRemove.clicked(null) + fillMode: Image.PreserveAspectFit source: "qrc:///images/trash_icon_all.svg" MouseArea { + id: mouseAreaRemoveAll + anchors.fill: parent - anchors.margins: -Utils.dp(8) + anchors.margins: -8 + onClicked: logControls.removeAll() } } diff --git a/resources/qml/Governikus/FeedbackView/+mobile/LogView.qml b/resources/qml/Governikus/FeedbackView/+mobile/LogView.qml new file mode 100644 index 0000000..1a40f52 --- /dev/null +++ b/resources/qml/Governikus/FeedbackView/+mobile/LogView.qml @@ -0,0 +1,127 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.LogModel 1.0 + + +SectionPage +{ + id: root + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + //: LABEL ANDROID IOS + title: qsTr("Log") + SettingsModel.translationTrigger + rightTitleBarAction: LogTitleBarControls { + allowRemove: comboBox.currentIndex > 0 + allowRemoveAll: comboBox.model.length > 1 // comboBox.count doesn't seem to update reliably + + onShare: LogModel.shareLog(popupPosition) + + onRemove: { + confirmationPopup.deleteAll = false + confirmationPopup.open() + } + + onRemoveAll: { + confirmationPopup.deleteAll = true + confirmationPopup.open() + } + } + + sectionPageFlickable: logView + + Item { + id: logSelector + height: comboBox.height + width: parent.width - 2 * Constants.pane_padding + anchors.top: parent.top + anchors.topMargin: Constants.pane_padding + anchors.horizontalCenter: parent.horizontalCenter + + GText { + id: comboText + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + //: LABEL ANDROID IOS + text: qsTr("Log:") + SettingsModel.translationTrigger + } + + GComboBox { + id: comboBox + + width: parent.width - comboText.width - Constants.component_spacing + anchors.right: parent.right + + Accessible.description: qsTr("Select logfile from list.") + SettingsModel.translationTrigger + + model: LogModel.logFiles + onActivated: LogModel.setLogfile(comboBox.currentIndex) + } + } + + Pane { + id: pane + + anchors { + top: logSelector.bottom + bottom: parent.bottom + left: parent.left + right: parent.right + margins: Constants.pane_padding + } + + clip: true + + GListView { + id: logView + + height: pane.height - 2 * Constants.pane_padding + width: pane.width - Constants.pane_padding + displayMarginBeginning: Constants.pane_padding + displayMarginEnd: Constants.pane_padding + + model: LogModel + + delegate: GText { + width: logView.width - Constants.pane_padding + + Accessible.onScrollDownAction: logView.scrollPageDown() + Accessible.onScrollUpAction: logView.scrollPageUp() + + bottomPadding: index % 2 ? Constants.groupbox_spacing : 0 + text: model.display + textStyle: index % 2 ? Style.text.hint : Style.text.hint_secondary + } + + Connections { + target: LogModel + onFireNewLogMsg: if (logView.atYEnd) logView.positionViewAtEnd() + } + } + } + + ConfirmationPopup { + id: confirmationPopup + + property bool deleteAll: true + + //: LABEL ANDROID IOS + title: (deleteAll ? qsTr("Delete all") : qsTr("Delete")) + SettingsModel.translationTrigger + //: INFO ANDROID IOS The current/all logfile(s) are about to be removed, user confirmation required. + text: (deleteAll ? qsTr("Please confirm that you want to delete your old logfiles.") + : qsTr("Please confirm that you want to delete the logfile.") + ) + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + okButtonText: qsTr("Delete") + SettingsModel.translationTrigger + onConfirmed: deleteAll ? LogModel.removeOtherLogfiles() : LogModel.removeCurrentLogfile() + } +} diff --git a/resources/qml/Governikus/FeedbackView/+mobile/StoreFeedbackPopup.qml b/resources/qml/Governikus/FeedbackView/+mobile/StoreFeedbackPopup.qml deleted file mode 100644 index 460b6d2..0000000 --- a/resources/qml/Governikus/FeedbackView/+mobile/StoreFeedbackPopup.qml +++ /dev/null @@ -1,67 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 - -import Governikus.Type.ApplicationModel 1.0 - - -Popup { - id: popup - - modal: true - focus: true - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent - - width: Utils.dp(350) - height: contentColumn.height + 2 * Constants.pane_padding - padding: Constants.pane_padding - - Column { - id: contentColumn - width: parent.width - spacing: Constants.pane_spacing - - Text { - id: header - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - font.bold: true - wrapMode: Text.WordWrap - text: qsTr("Would you like to rate this app?") + settingsModel.translationTrigger - } - - Text { - id: info - color: Constants.secondary_text - width: parent.width - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - text: qsTr("We would be very grateful if you could leave a rating on the Google Play Store!") + settingsModel.translationTrigger - } - - Row { - width: parent.width - spacing: Constants.pane_spacing - - GButton { - text: qsTr("No, Thanks") + settingsModel.translationTrigger - width: (parent.width - Constants.pane_spacing) / 2 - - onClicked: { - close() - } - } - - GButton { - text: qsTr("Rate") + settingsModel.translationTrigger - width: (parent.width - Constants.pane_spacing) / 2 - - onClicked: { - Qt.openUrlExternally("market://details?id=" + ApplicationModel.packageName) - close() - } - } - } - } -} diff --git a/resources/qml/Governikus/FeedbackView/qmldir b/resources/qml/Governikus/FeedbackView/qmldir index 3dd910f..e2f874c 100644 --- a/resources/qml/Governikus/FeedbackView/qmldir +++ b/resources/qml/Governikus/FeedbackView/qmldir @@ -3,5 +3,5 @@ module FeedbackView internal LogTitleBarControls LogTitleBarControls.qml Feedback 1.0 Feedback.qml -Log 1.0 Log.qml +LogView 1.0 LogView.qml StoreFeedbackPopup 1.0 StoreFeedbackPopup.qml diff --git a/resources/qml/Governikus/Global/+desktop/+mac/BrandConstants.qml b/resources/qml/Governikus/Global/+desktop/+mac/BrandConstants.qml index 6406eff..c4387f4 100644 --- a/resources/qml/Governikus/Global/+desktop/+mac/BrandConstants.qml +++ b/resources/qml/Governikus/Global/+desktop/+mac/BrandConstants.qml @@ -1,6 +1,8 @@ -import QtQuick 2.10 +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ -import Governikus.Type.ApplicationModel 1.0 +import QtQuick 2.10 Item { } diff --git a/resources/qml/Governikus/Global/+desktop/+win/BrandConstants.qml b/resources/qml/Governikus/Global/+desktop/+win/BrandConstants.qml index 6406eff..c4387f4 100644 --- a/resources/qml/Governikus/Global/+desktop/+win/BrandConstants.qml +++ b/resources/qml/Governikus/Global/+desktop/+win/BrandConstants.qml @@ -1,6 +1,8 @@ -import QtQuick 2.10 +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ -import Governikus.Type.ApplicationModel 1.0 +import QtQuick 2.10 Item { } diff --git a/resources/qml/Governikus/Global/+desktop/ConfirmationPopup.qml b/resources/qml/Governikus/Global/+desktop/ConfirmationPopup.qml new file mode 100644 index 0000000..8b4ea11 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/ConfirmationPopup.qml @@ -0,0 +1,41 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + + +BaseConfirmationPopup { + id: root + + buttons: Row { + width: parent.width + + layoutDirection: Qt.RightToLeft + spacing: Constants.component_spacing + bottomPadding: Constants.pane_padding + rightPadding: Constants.pane_padding + + GButton { + visible: style & ConfirmationPopup.PopupStyle.OkButton + + text: root.okButtonText + + onClicked: root.accept() + } + + GButton { + visible: style & ConfirmationPopup.PopupStyle.CancelButton + + text: root.cancelButtonText + + onClicked: root.cancel() + } + } +} + diff --git a/resources/qml/Governikus/Global/+desktop/ContinueButton.qml b/resources/qml/Governikus/Global/+desktop/ContinueButton.qml deleted file mode 100644 index 3637884..0000000 --- a/resources/qml/Governikus/Global/+desktop/ContinueButton.qml +++ /dev/null @@ -1,37 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.View 1.0 - - -Button { - id: control - width: height - - Accessible.role: Accessible.Button - Accessible.name: qsTr("Continue") - - FocusFrame {} - - background: Rectangle { - anchors.fill: parent - anchors.margins: control.down ? control.height / 32 : 0 - radius: height / 2 - border.width: height / 40; - border.color: Constants.white - color: "transparent" - - Rectangle { - radius: height / 2 - anchors.fill: parent - anchors.margins: parent.height / 8; - color: Qt.lighter(Constants.blue, 1.1) - - Image { - anchors.centerIn: parent - sourceSize.height: parent.height / 2; - source: "qrc:///images/desktop/continue_arrow.svg" - } - } - } -} diff --git a/resources/qml/Governikus/Global/+desktop/GButton.qml b/resources/qml/Governikus/Global/+desktop/GButton.qml index e4ec061..062006a 100644 --- a/resources/qml/Governikus/Global/+desktop/GButton.qml +++ b/resources/qml/Governikus/Global/+desktop/GButton.qml @@ -1,43 +1,61 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 Button { id: control - padding: Constants.pane_padding / 3 - contentItem: Row { + property var textStyle: enabled ? Style.text.button : Style.text.button_disabled + property color buttonColor: Style.color.accent + property alias cursorShape: mouseArea.cursorShape + + padding: Constants.pane_padding / 2 + + contentItem: RowLayout { spacing: Constants.groupbox_spacing Image { - source: control.icon.source visible: source.toString().length > 0 - sourceSize.height: textContainer.height + sourceSize.height: ApplicationModel.scaleFactor * 40 + + source: control.icon.source } Item { id: textContainer - height: originalSize.height - width: originalSize.width - Text { + Layout.fillWidth: true + implicitHeight: originalSize.implicitHeight + implicitWidth: originalSize.implicitWidth + + GText { id: originalSize + visible: false + anchors.fill: parent + text: control.text - font.bold: true - font.pixelSize: Constants.normal_font_size + textStyle: control.textStyle } - Text { + GText { + anchors.fill: parent + text: control.text - color: Constants.white - font.bold: true - font.pixelSize: Constants.normal_font_size - ApplicationModel.scaleFactor * (control.down ? 1 : 0) - anchors.centerIn: parent + textStyle: control.textStyle + font.pixelSize: textStyle.textSize - ApplicationModel.scaleFactor * (control.down ? 1 : 0) + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter FocusFrame { scope: control @@ -47,7 +65,16 @@ Button { } background: Rectangle { - color: Constants.blue - radius: ApplicationModel.scaleFactor * 4 + color: enabled ? buttonColor : Style.color.accent_disabled + radius: Style.dimens.button_radius + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + + onPressed: mouse.accepted = false + cursorShape: Qt.PointingHandCursor } } diff --git a/resources/qml/Governikus/Global/+desktop/GCheckBox.qml b/resources/qml/Governikus/Global/+desktop/GCheckBox.qml index 39c0b40..185055b 100644 --- a/resources/qml/Governikus/Global/+desktop/GCheckBox.qml +++ b/resources/qml/Governikus/Global/+desktop/GCheckBox.qml @@ -1,24 +1,37 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Type.ApplicationModel 1.0 CheckBox { id: control + implicitHeight: ApplicationModel.scaleFactor * 26 + implicitWidth: implicitHeight + indicator: Rectangle { anchors.fill: parent - color: Constants.blue + + color: control.checked ? (enabled ? Style.color.accent : Style.color.accent_disabled) : Style.color.transparent radius: Math.max(ApplicationModel.scaleFactor * 4, 1) + border.width: Math.max(ApplicationModel.scaleFactor * 4, 1) + border.color: enabled ? Style.color.accent : Style.color.accent_disabled Image { - source: "qrc:///images/check.svg" + visible: control.checked + anchors.fill: parent anchors.margins: Math.max(ApplicationModel.scaleFactor * 4, 1) + + source: "qrc:///images/check.svg" fillMode: Image.PreserveAspectFit - visible: control.checked } } } diff --git a/resources/qml/Governikus/Global/+desktop/GComboBox.qml b/resources/qml/Governikus/Global/+desktop/GComboBox.qml new file mode 100644 index 0000000..ad6818f --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/GComboBox.qml @@ -0,0 +1,61 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.View 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 + +ComboBox { + id: control + + property var textStyle: Style.text.normal_inverse + + spacing: Constants.groupbox_spacing + font.pixelSize: textStyle.fontSize + + indicator: Image { + visible: control.model.length > 1 || control.model.count > 1 + x: control.width - width - control.rightPadding + y: control.topPadding + (control.availableHeight - height) / 2 + + source: "qrc:///images/triangle.svg" + sourceSize.width: ApplicationModel.scaleFactor * 20 + sourceSize.height: ApplicationModel.scaleFactor * 20 + } + + contentItem: GText { + padding: control.spacing + rightPadding: control.indicator.width + control.spacing + text: control.displayText + textStyle: control.textStyle + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + delegate: ItemDelegate { + width: control.width + highlighted: control.highlightedIndex === index + + GText { + anchors.verticalCenter: parent.verticalCenter + leftPadding: ApplicationModel.scaleFactor * 5 + + text: modelData + textStyle: control.textStyle + font.bold: control.currentIndex === index + } + } + + FocusFrame { + scope: control + marginFactor: 1.5 + } + + background: Rectangle { + border.color: Constants.black + } +} diff --git a/resources/qml/Governikus/Global/+desktop/GText.qml b/resources/qml/Governikus/Global/+desktop/GText.qml deleted file mode 100644 index c5b278c..0000000 --- a/resources/qml/Governikus/Global/+desktop/GText.qml +++ /dev/null @@ -1,5 +0,0 @@ -import QtQuick 2.10 - -Text { - renderType: Text.NativeRendering -} diff --git a/resources/qml/Governikus/Global/+desktop/GTextField.qml b/resources/qml/Governikus/Global/+desktop/GTextField.qml index 4b6ad7f..e9929f7 100644 --- a/resources/qml/Governikus/Global/+desktop/GTextField.qml +++ b/resources/qml/Governikus/Global/+desktop/GTextField.qml @@ -1,14 +1,26 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 +import Governikus.Style 1.0 +import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 Item { + id: baseItem + property alias text: field.text - property alias font: field.font + property var textStyle: Style.text.normal property alias displayText: field.displayText property alias echoMode: field.echoMode + property alias textAnchors: field.anchors + property alias validator: field.validator + property alias maximumLength: field.maximumLength + readonly property alias acceptableInput: field.acceptableInput signal accepted property bool valid: true @@ -18,24 +30,34 @@ Item { Rectangle { id: textBackground - radius: ApplicationModel.scaleFactor * 6 + anchors.fill: parent + + radius: ApplicationModel.scaleFactor * 6 + color: enabled ? Constants.white : Constants.grey border.color: Constants.red - color: enabled ? "white" : Constants.grey border.width: valid ? 0 : ApplicationModel.scaleFactor * 2 } TextField { id: field + anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.margins: textBackground.radius + + activeFocusOnTab: true padding: 0 - font.pixelSize: Constants.normal_font_size - onAccepted: parent.accepted() + font.pixelSize: baseItem.textStyle.textSize + color: baseItem.textStyle.textColor + onAccepted: baseItem.accepted() background: Item {} } onActiveFocusChanged: if (focus) field.forceActiveFocus() + + FocusFrame { + scope: field + } } diff --git a/resources/qml/Governikus/Global/+desktop/LabeledText.qml b/resources/qml/Governikus/Global/+desktop/LabeledText.qml index ae27d5d..61f487e 100644 --- a/resources/qml/Governikus/Global/+desktop/LabeledText.qml +++ b/resources/qml/Governikus/Global/+desktop/LabeledText.qml @@ -1,32 +1,61 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import Governikus.View 1.0 +import Governikus.Global 1.0 +import Governikus.Style 1.0 + + +Item { + id: baseItem + signal linkActivated(string link) -Column { property alias label: label.text property alias text: body.text property alias textFormat: body.textFormat property alias textUppercase: body.font.capitalization + property alias underlineLabel: label.font.underline + readonly property double focusFrameMargins: focusFrame.anchors.margins - signal linkActivated(string link) + implicitHeight: column.implicitHeight + implicitWidth: Math.max(label.implicitWidth, body.implicitWidth) - width: Math.max(label.implicitWidth, body.implicitWidth) + Accessible.role: Accessible.Section + Accessible.name: label.text + (label.text.trim().endsWith(":") ? " " : ": ") + body.text - Text { - id: label - anchors.left: parent.left - anchors.right: parent.right - font.pixelSize: Constants.normal_font_size - color: Constants.blue - wrapMode: Text.WordWrap + FocusFrame { + id: focusFrame + dynamic: false + border.color: Constants.black } - Text { - id: body - color: Constants.black - anchors.left: parent.left - anchors.right: parent.right - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - onLinkActivated: parent.linkActivated(link) + Column { + id: column + + anchors.fill: parent + + GText { + id: label + + visible: label.text !== "" + + width: parent.width + + textStyle: Style.text.normal_accent + } + + GText { + id: body + + visible: body.text !== "" + + width: parent.width + + textStyle: Style.text.normal_inverse + onLinkActivated: baseItem.linkActivated(link) + } } } diff --git a/resources/qml/Governikus/Global/+desktop/LocationButton.qml b/resources/qml/Governikus/Global/+desktop/LocationButton.qml new file mode 100644 index 0000000..02d516a --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/LocationButton.qml @@ -0,0 +1,24 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +GButton { + id: root + + property string language + property alias image: root.icon.source + + cursorShape: SettingsModel.language === language ? Qt.ArrowCursor : Qt.PointingHandCursor + opacity: SettingsModel.language === language ? 1.0 : 0.5 + onClicked: SettingsModel.language = language +} diff --git a/resources/qml/Governikus/Global/+desktop/NavigationButton.qml b/resources/qml/Governikus/Global/+desktop/NavigationButton.qml new file mode 100644 index 0000000..0d7bdd4 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/NavigationButton.qml @@ -0,0 +1,60 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Button { + id: control + + property int buttonType // Qt.ForwardButton || Qt.BackButton + + width: ApplicationModel.scaleFactor * 160 + height: ApplicationModel.scaleFactor * 160 + + Accessible.role: Accessible.Button + //: LABEL DESKTOP_QML + Accessible.name: { + if (buttonType === Qt.ForwardButton) { + return qsTr("Continue") + SettingsModel.translationTrigger + } + console.assert(buttonType === Qt.BackButton, "Use either Qt.ForwardButton or Qt.BackButton") + return qsTr("Back") + SettingsModel.translationTrigger + } + + background: Rectangle { + anchors.fill: parent + anchors.margins: control.down ? control.height / 32 : 0 + + radius: height / 2 + border.width: height / 40; + border.color: enabled ? Constants.white : Constants.red + color: Style.color.transparent + + Rectangle { + anchors.fill: parent + anchors.margins: parent.height / 8; + + radius: height / 2 + color: Qt.lighter(Constants.blue, 1.1) + + Image { + anchors.centerIn: parent + + source: "qrc:///images/desktop/continue_arrow.svg" + sourceSize.height: parent.height / 2; + transformOrigin: Item.Center + rotation: buttonType === Qt.BackButton ? 180 : 0 + } + } + } + + FocusFrame {} +} diff --git a/resources/qml/Governikus/Global/+desktop/Pane.qml b/resources/qml/Governikus/Global/+desktop/Pane.qml index 5dc37b1..7708a94 100644 --- a/resources/qml/Governikus/Global/+desktop/Pane.qml +++ b/resources/qml/Governikus/Global/+desktop/Pane.qml @@ -1,21 +1,60 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 +import Governikus.View 1.0 +import Governikus.Style 1.0 import Governikus.Type.ApplicationModel 1.0 -Rectangle { +Item { id: root - property alias title: titleText.text - default property alias paneChildren: paneContent.children - anchors.left: parent.left - anchors.right: parent.right - height: childrenRect.height - color: Constants.white - radius: ApplicationModel.scaleFactor * 10 + Accessible.role: Accessible.Heading + Accessible.name: titleText.text + + property alias title: titleText.text + property alias titleTextStyle: titleText.textStyle + + property alias checkBox: titleText.checkBox + property alias content: paneContent + default property alias children: paneContent.children + + implicitHeight: containerCol.implicitHeight + implicitWidth: containerCol.implicitWidth + + Keys.onPressed: if (event.key === Qt.Key_Space) checkBox.toggle() + + Item { + id: shadowLayer + + anchors.fill: parent + + layer.enabled: true + layer.effect: DropShadow { + radius: 8 + samples: 8 + source: background + color: Qt.darker(Constants.grey, 1.2) + verticalOffset: 2 + } + } + + Rectangle { + id: background + + anchors.fill: parent + + color: Style.color.background_pane + radius: Style.dimens.corner_radius + } Column { + id: containerCol + anchors.left: parent.left anchors.right: parent.right anchors.leftMargin: Constants.pane_padding @@ -26,21 +65,22 @@ Rectangle { PaneTitle { id: titleText + + width: Math.max(implicitWidth + 2 * Constants.pane_padding, parent.width) + + FocusFrame { + scope: root + dynamic: false + border.color: Constants.black + } } Column { id: paneContent + width: parent.width spacing: Constants.pane_spacing } } - layer.enabled: true - layer.effect: DropShadow { - radius: 8 - samples: 8 - source: root - color: Qt.darker(Constants.grey, 1.2) - verticalOffset: 2 - } } diff --git a/resources/qml/Governikus/Global/+desktop/PaneTitle.qml b/resources/qml/Governikus/Global/+desktop/PaneTitle.qml new file mode 100644 index 0000000..34224d8 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/PaneTitle.qml @@ -0,0 +1,32 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Style 1.0 + +GText { + property alias checkBox: optionState + + visible: text !== "" + + textStyle: Style.text.header_accent + + GCheckBox { + id: optionState + + visible: false + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + + activeFocusOnTab: false + } + MouseArea { + anchors.fill: parent + + onReleased: if (mouse.button === Qt.LeftButton) optionState.toggle() + cursorShape: optionState.visible ? Qt.PointingHandCursor : Qt.ArrowCursor + } +} diff --git a/resources/qml/Governikus/Global/+desktop/PlatformConstants.qml b/resources/qml/Governikus/Global/+desktop/PlatformConstants.qml index 3193a0e..2c85e04 100644 --- a/resources/qml/Governikus/Global/+desktop/PlatformConstants.qml +++ b/resources/qml/Governikus/Global/+desktop/PlatformConstants.qml @@ -1,28 +1,19 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Type.ApplicationModel 1.0 BrandConstants { - readonly property color blue: Qt.darker(background_color, 1.2) - readonly property color background_color: "#659bcd" - readonly property color grey_light: "#bbbbbb" - readonly property color secondary_text: "#dadada" - readonly property color accent_color: "#7879b2" - - readonly property int header_font_size: ApplicationModel.scaleFactor * 42 - readonly property int pane_title_font_size: ApplicationModel.scaleFactor * 32 - readonly property int normal_font_size: ApplicationModel.scaleFactor * 26 - readonly property int label_font_size: ApplicationModel.scaleFactor * 20 - readonly property int small_font_size: ApplicationModel.scaleFactor * 18 - - readonly property int titlebar_padding: ApplicationModel.scaleFactor * 20 - readonly property int titlebar_spacing: ApplicationModel.scaleFactor * 20 - readonly property int titlebar_font_size: ApplicationModel.scaleFactor * 24 + readonly property color blue: "#5481ab" readonly property int component_spacing: ApplicationModel.scaleFactor * 30 readonly property int pane_padding: ApplicationModel.scaleFactor * 30 readonly property int pane_spacing: ApplicationModel.scaleFactor * 30 readonly property int groupbox_spacing: ApplicationModel.scaleFactor * 20 + readonly property int text_spacing: ApplicationModel.scaleFactor * 10 readonly property bool is_desktop: true } diff --git a/resources/qml/Governikus/Global/+desktop/RoundedRectangle.qml b/resources/qml/Governikus/Global/+desktop/RoundedRectangle.qml new file mode 100644 index 0000000..aecf01e --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/RoundedRectangle.qml @@ -0,0 +1,99 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + +Item { + id: roundedRectangle + + property real radius: Style.dimens.corner_radius + property real borderWidth: 0 + + property color color: Style.color.background + property color borderColor: Style.color.border + + property bool topLeftCorner: true + property bool topRightCorner: true + property bool bottomLeftCorner: true + property bool bottomRightCorner: true + + onVisibleChanged: canvas.requestPaint() + onRadiusChanged: canvas.requestPaint() + onBorderWidthChanged: canvas.requestPaint() + onBorderColorChanged: canvas.requestPaint() + onColorChanged: canvas.requestPaint() + onTopLeftCornerChanged: canvas.requestPaint() + onTopRightCornerChanged: canvas.requestPaint() + onBottomRightCornerChanged: canvas.requestPaint() + onBottomLeftCornerChanged: canvas.requestPaint() + + + Canvas { + id: canvas + + anchors.fill: parent + + onPaint: { + let context = getContext("2d") + if (context === null) { + return + } + + context.save() + + context.reset() + + context.beginPath() + + context.moveTo(0, height / 2) + + if (topLeftCorner){ + context.lineTo(0, radius) + context.arcTo(0, 0, radius, 0, radius) + } else { + context.lineTo(0, 0) + } + + if (topRightCorner){ + context.lineTo(width - radius, 0) + context.arcTo(width, 0, width, radius, radius) + } else { + context.lineTo(width, 0) + } + + if (bottomRightCorner) { + context.lineTo(width, height - radius) + context.arcTo(width, height, width - radius, height, radius) + } else { + context.lineTo(width, height) + } + + if (bottomLeftCorner) { + context.lineTo(radius, height) + context.arcTo(0, height, 0, height - radius, radius) + } else { + context.lineTo(0, height) + } + + context.lineTo(height / 2) + context.closePath() + + context.fillStyle = color + context.fill() + + if (borderWidth > 0) { + context.clip() + context.lineWidth = borderWidth * 2 + context.strokeStyle = borderColor + context.stroke() + } + + context.restore() + } + } + +} diff --git a/resources/qml/Governikus/Global/+desktop/ScrollGradients.qml b/resources/qml/Governikus/Global/+desktop/ScrollGradients.qml new file mode 100644 index 0000000..3cab396 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/ScrollGradients.qml @@ -0,0 +1,47 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + + +Item { + id: baseItem + + property color color + + Rectangle { + height: Constants.pane_padding + anchors { + top: parent.top + left: parent.left + right: parent.right + leftMargin: Constants.pane_padding + rightMargin: Constants.pane_padding + } + + gradient: Gradient { + GradientStop { position: 0.0; color: baseItem.color } + GradientStop { position: 1.0; color: Style.color.transparent } + } + } + + Rectangle { + height: Constants.pane_padding + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + leftMargin: Constants.pane_padding + rightMargin: Constants.pane_padding + } + + gradient: Gradient { + GradientStop { position: 0.0; color: Style.color.transparent } + GradientStop { position: 1.0; color: baseItem.color } + } + } +} diff --git a/resources/qml/Governikus/Global/+desktop/ScrollablePane.qml b/resources/qml/Governikus/Global/+desktop/ScrollablePane.qml new file mode 100644 index 0000000..aea86e8 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/ScrollablePane.qml @@ -0,0 +1,123 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.0 + +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Item { + id: root + + function scrollYPositionIntoView(pYposition) { + var availableFlickableHeight = flickable.height - paneContent.anchors.margins + var dy = pYposition - flickable.contentY - availableFlickableHeight + + if (dy > 0 || flickable.contentY > 0) { + flickable.contentY += dy + + if (flickable.contentY < 0) + flickable.contentY = 0 + else if (flickable.contentY + availableFlickableHeight > flickable.contentHeight) + flickable.contentY = flickable.contentHeight - availableFlickableHeight + } + } + + Accessible.role: Accessible.Heading + Accessible.name: titleText.text + + property alias title: titleText.text + property alias content: paneContent + default property alias children: paneContent.children + + Item { + id: shadowLayer + + anchors.fill: parent + + layer.enabled: true + layer.effect: DropShadow { + radius: 8 + samples: 8 + source: background + color: Qt.darker(Constants.grey, 1.2) + verticalOffset: 2 + } + } + + Rectangle { + id: background + + anchors.fill: parent + + color: Style.color.background_pane + radius: Style.dimens.corner_radius + } + + Item { + anchors.fill: parent + + clip: true + + Flickable { + id: flickable + + anchors.fill: parent + anchors.topMargin: Constants.pane_padding + anchors.leftMargin: Constants.pane_padding + anchors.bottomMargin: Constants.pane_padding + + boundsBehavior: Flickable.StopAtBounds + contentHeight: contentColumn.implicitHeight + ScrollBar.vertical: ScrollBar { + policy: size === 1.0 ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn + } + + Column { + id: contentColumn + + anchors.fill: parent + anchors.rightMargin: Constants.pane_padding + + spacing: Constants.pane_spacing + + GText { + id: titleText + + visible: text !== "" + width: parent.width + + textStyle: Style.text.header_accent + elide: Text.ElideRight + + FocusFrame { + scope: root + dynamic: false + border.color: Constants.black + } + } + + Column { + id: paneContent + + width: parent.width + + spacing: Constants.pane_spacing + } + } + } + + ScrollGradients { + anchors.fill: parent + color: background.color + } + } + +} diff --git a/resources/qml/Governikus/Global/+desktop/SearchBar.qml b/resources/qml/Governikus/Global/+desktop/SearchBar.qml new file mode 100644 index 0000000..0fa994a --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/SearchBar.qml @@ -0,0 +1,80 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +GTextField { + id: searchField + + function clear() { + searchField.text = "" + searchField.forceActiveFocus() + } + + width: 400 * ApplicationModel.scaleFactor + + Accessible.name: qsTr("Search") + SettingsModel.translationTrigger + + textAnchors.leftMargin: glassIcon.width + Constants.groupbox_spacing + textAnchors.rightMargin: iconItem.width + Constants.groupbox_spacing + + textStyle: Style.text.normal_inverse + + Image { + id: glassIcon + + sourceSize.height: parent.height - 2 * anchors.leftMargin + sourceSize.width: height + anchors.left: parent.left + anchors.leftMargin: parent.height / 8 + anchors.verticalCenter: parent.verticalCenter + + source: "qrc:///images/search_icon.svg" + } + + GText { + visible: searchField.text === "" + anchors.centerIn: searchField + + //: LABEL DESKTOP_QML + text: qsTr("Search") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary_inverse + } + + Image { + id: iconItem + + visible: parent.displayText !== "" + height: parent.height - 2 * anchors.rightMargin + width: height + anchors.right: parent.right + anchors.rightMargin: parent.height / 8 + anchors.verticalCenter: parent.verticalCenter + + activeFocusOnTab: true + Accessible.role: Accessible.Button + Accessible.name: qsTr("Clear") + SettingsModel.translationTrigger + + source: "qrc:///images/search_cancel.svg" + Keys.onSpacePressed: searchField.clear() + + MouseArea { + anchors.fill: parent + + onClicked: searchField.clear() + } + + FocusFrame { + border.color: Constants.black + } + } +} diff --git a/resources/qml/Governikus/Global/+desktop/TabbedPane.qml b/resources/qml/Governikus/Global/+desktop/TabbedPane.qml new file mode 100644 index 0000000..a542a71 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/TabbedPane.qml @@ -0,0 +1,244 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.View 1.0 + + +Item { + id: root + + property var sectionsModel: undefined + property var contentObjectModel: undefined + property Component contentDelegate: null + property Component footerItem: null + property Component sectionDelegate: TabbedPaneDelegateOneLineText { + sectionName: model ? (model.display ? model.display : model.modelData) : "" + } + property alias currentIndex: sectionNameList.currentIndex + property alias sectionCount: sectionNameList.count + + readonly property var currentItemModel: sectionNameList.currentItem ? sectionNameList.currentItem.itemModel : null + readonly property int horizonalSeparatorHeight: Math.max(ApplicationModel.scaleFactor * 1, 1) + readonly property int verticalSeparatorWidth: Math.max(ApplicationModel.scaleFactor * 4, 1) + readonly property real relativeListViewWidth: 0.3 + readonly property int availableHeight: height - 2 * contentPadding + property var contentPadding: Constants.pane_padding + + function scrollYPositionIntoView(pYposition) { + var dy = pYposition - flickable.contentY - flickable.height + + if (dy > 0 || flickable.contentY > 0) { + flickable.contentY += dy + + if (flickable.contentY < 0) + flickable.contentY = 0 + else if (flickable.contentY + flickable.height > flickable.contentHeight) + pFlickable.contentY = flickable.contentHeight - flickable.height + } + } + + Row { + anchors.fill: parent + + ColumnLayout { + height: parent.height + width: parent.width * relativeListViewWidth + + spacing: Constants.component_spacing + + ListView { + id: sectionNameList + + Layout.fillWidth: true + Layout.fillHeight: true + + activeFocusOnTab: true + + clip: true + boundsBehavior: Flickable.StopAtBounds + model: sectionsModel + highlightFollowsCurrentItem: true + highlight: null + delegate: sectionNameDelegate + } + + Loader { + Layout.preferredHeight: status === Loader.Ready ? item.height : 0 + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + + sourceComponent: footerItem + } + } + + RoundedRectangle { + height: parent.height + width: parent.width * (1.0 - relativeListViewWidth) + + radius: Style.dimens.corner_radius + color: Style.color.background_pane_active + topLeftCorner: false + bottomLeftCorner: sectionNameList.contentHeight < height + clip: true + + Flickable { + id: flickable + + anchors { + fill: parent + bottomMargin: contentPadding + topMargin: contentPadding + } + + ScrollBar.vertical: ScrollBar { + policy: size === 1.0 ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn + } + contentHeight: contentLoader.item.height + boundsBehavior: Flickable.StopAtBounds + + Loader { + id: contentLoader + + anchors { + top: parent.top + left: parent.left + right: parent.right + leftMargin: contentPadding + rightMargin: contentPadding + } + + sourceComponent: { + if (contentDelegate !== null) { + return contentDelegate + } + + if (contentObjectModel === undefined) { + return null + } + + if (sectionNameList.currentIndex < contentObjectModel.count){ + return contentObjectModel.get(sectionNameList.currentIndex) + } + } + } + } + + ScrollGradients { + anchors.fill: parent + color: parent.color + } + } + } + + + Component { + id: sectionNameDelegate + + Control { + id: delegateItem + + readonly property bool isFirstItem: index === 0 + readonly property bool isLastItem: index === ListView.view.count - 1 + readonly property bool isCurrentItem: ListView.isCurrentItem + readonly property bool isPreviousToCurrentItem: index === ListView.view.currentIndex - 1 + readonly property var itemModel: model + + Accessible.role: Accessible.Grouping + + width: parent.width + height: delegateLoader.height + 2 * Constants.pane_padding + + RoundedRectangle { + id: background + + anchors.fill: parent + + radius: Style.dimens.corner_radius + color: isCurrentItem ? Style.color.background_pane_active : Style.color.background_pane + + topLeftCorner: isFirstItem + topRightCorner: false + bottomRightCorner: false + bottomLeftCorner: isLastItem + } + + Rectangle { + id: horizontalSeparator + + visible: !isLastItem + + width: parent.width + height: isCurrentItem || isPreviousToCurrentItem ? verticalSeparatorWidth : horizonalSeparatorHeight + anchors.bottom: parent.bottom + + color: isCurrentItem || isPreviousToCurrentItem ? Style.color.background : Style.color.border + } + + Rectangle { + id: verticalSeparator + + height: parent.height + width: verticalSeparatorWidth + anchors.right: parent.right + + color: !isCurrentItem ? Style.color.background : Style.color.background_pane_active + } + + Rectangle { + id: missingPiece + + visible: !isLastItem + + height: isCurrentItem ? verticalSeparatorWidth : horizonalSeparatorHeight + width: verticalSeparatorWidth + anchors { + right: parent.right + bottom: parent.bottom + } + + color: Style.color.background + } + + FocusFrame { + visible: delegateItem.focusReason !== Qt.MouseFocusReason + + framee: delegateLoader + border.color: Constants.black + dynamic: false + } + + Loader { + id: delegateLoader + + property var model: itemModel + + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + right: parent.right + leftMargin: Constants.pane_padding + rightMargin: Constants.pane_padding + } + + sourceComponent: sectionDelegate + } + + MouseArea { + anchors.fill: parent + + cursorShape: index === currentIndex ? Qt.ArrowCursor : Qt.PointingHandCursor + onClicked: { + delegateItem.ListView.view.currentIndex = index + } + } + } + } +} diff --git a/resources/qml/Governikus/Global/+desktop/TabbedPaneDelegateIconAndText.qml b/resources/qml/Governikus/Global/+desktop/TabbedPaneDelegateIconAndText.qml new file mode 100644 index 0000000..c79f51a --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/TabbedPaneDelegateIconAndText.qml @@ -0,0 +1,41 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 + +Row { + id: root + + property string sectionName + property string iconPath + + Accessible.role: Accessible.PageTab + Accessible.name: sectionName + + spacing: Constants.groupbox_spacing + + Image { + id: sectionIcon + + visible: source !== "" + sourceSize.height: ApplicationModel.scaleFactor * 60 + sourceSize.width: ApplicationModel.scaleFactor * 60 + + source: iconPath + fillMode: Image.PreserveAspectFit + } + + GText { + width: parent.width - sectionIcon.width + anchors.verticalCenter: parent.verticalCenter + + text: sectionName + textStyle: Style.text.header_inverse + maximumLineCount: 1 + elide: Text.ElideRight + } +} diff --git a/resources/qml/Governikus/Global/+desktop/TabbedPaneDelegateIconAndThreeLineText.qml b/resources/qml/Governikus/Global/+desktop/TabbedPaneDelegateIconAndThreeLineText.qml new file mode 100644 index 0000000..aab428c --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/TabbedPaneDelegateIconAndThreeLineText.qml @@ -0,0 +1,68 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.2 + +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 + +RowLayout { + id: root + + property string mainText + property string headerText + property string footerText + property string iconPath + + Accessible.role: Accessible.PageTab + Accessible.name: headerText + " " + mainText + " " + footerText + + width: parent.width + + spacing: Constants.groupbox_spacing + + Image { + id: sectionIcon + + visible: source !== "" + sourceSize.height: ApplicationModel.scaleFactor * 60 + sourceSize.width: ApplicationModel.scaleFactor * 60 + + source: iconPath + fillMode: Image.PreserveAspectFit + } + + Column { + Layout.fillWidth: true + + spacing: Constants.text_spacing + + GText { + width: parent.width + + text: headerText + textStyle: Style.text.normal_inverse + maximumLineCount: 1 + } + + GText { + width: parent.width + + text: mainText + textStyle: Style.text.header_inverse + maximumLineCount: 1 + elide: Text.ElideRight + } + + GText { + width: parent.width + + text: footerText + textStyle: Style.text.normal_inverse + maximumLineCount: 1 + elide: Text.ElideRight + } + } +} diff --git a/resources/qml/Governikus/Global/+desktop/TabbedPaneDelegateOneLineText.qml b/resources/qml/Governikus/Global/+desktop/TabbedPaneDelegateOneLineText.qml new file mode 100644 index 0000000..1a1ffef --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/TabbedPaneDelegateOneLineText.qml @@ -0,0 +1,23 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Style 1.0 + +GText { + id: root + + property string sectionName + + Accessible.role: Accessible.PageTab + Accessible.name: sectionName + + width: parent.width + + textStyle: Style.text.header_inverse + text: sectionName + maximumLineCount: 1 + elide: Text.ElideRight +} diff --git a/resources/qml/Governikus/Global/+desktop/ToggleableOption.qml b/resources/qml/Governikus/Global/+desktop/ToggleableOption.qml new file mode 100644 index 0000000..6c720e9 --- /dev/null +++ b/resources/qml/Governikus/Global/+desktop/ToggleableOption.qml @@ -0,0 +1,70 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +Item { + id: root + + property alias text: optionText.text + property alias checked: optionState.checked + + property TextStyle textStyle: enabled ? Style.text.normal_inverse : Style.text.normal_secondary_inverse + + implicitHeight: layout.implicitHeight + implicitWidth: layout.implicitWidth + + activeFocusOnTab: true + + Accessible.name: root.text + " " + (checked ? qsTr("is enabled") : qsTr("is disabled")) + SettingsModel.translationTrigger + Accessible.description: root.text + Accessible.role: Accessible.CheckBox + + Keys.onSpacePressed: optionState.toggle() + + RowLayout { + id: layout + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Constants.groupbox_spacing + + GCheckBox { + id: optionState + + activeFocusOnTab: false + } + + GText { + id: optionText + + Layout.fillWidth: true + activeFocusOnTab: false + + textStyle: root.textStyle + } + } + + FocusFrame { + border.color: optionText.color + dynamic: false + } + + MouseArea { + anchors.fill: parent + onClicked: optionState.toggle() + cursorShape: Qt.PointingHandCursor + } +} diff --git a/resources/qml/Governikus/Global/+mobile/+android/+phone/DeviceConstants.qml b/resources/qml/Governikus/Global/+mobile/+android/+phone/DeviceConstants.qml index 2f7c0dc..7c0ed0a 100644 --- a/resources/qml/Governikus/Global/+mobile/+android/+phone/DeviceConstants.qml +++ b/resources/qml/Governikus/Global/+mobile/+android/+phone/DeviceConstants.qml @@ -1,6 +1,8 @@ -import QtQuick 2.10 +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ -import "Utils.js" as Utils +import QtQuick 2.10 Item { readonly property bool is_tablet: false diff --git a/resources/qml/Governikus/Global/+mobile/+android/+tablet/DeviceConstants.qml b/resources/qml/Governikus/Global/+mobile/+android/+tablet/DeviceConstants.qml index 11a5f6c..4afbd01 100644 --- a/resources/qml/Governikus/Global/+mobile/+android/+tablet/DeviceConstants.qml +++ b/resources/qml/Governikus/Global/+mobile/+android/+tablet/DeviceConstants.qml @@ -1,6 +1,8 @@ -import QtQuick 2.10 +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ -import "Utils.js" as Utils +import QtQuick 2.10 Item { readonly property bool is_tablet: true diff --git a/resources/qml/Governikus/Global/+mobile/+android/BrandConstants.qml b/resources/qml/Governikus/Global/+mobile/+android/BrandConstants.qml index 0a230b0..6b8d648 100644 --- a/resources/qml/Governikus/Global/+mobile/+android/BrandConstants.qml +++ b/resources/qml/Governikus/Global/+mobile/+android/BrandConstants.qml @@ -1,22 +1,13 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 -import "Utils.js" as Utils - DeviceConstants { - readonly property color grey_light: "#bbbbbb" - readonly property color blue_dark: "#324d66" - readonly property color blue_light: "#659bcd" - readonly property color accent_color: "#7879b2" - readonly property color primary_text: "#ffffff" - readonly property color secondary_text: "#4a4a4a" - readonly property int titlebar_font_size: Utils.dp(18) - readonly property int provider_section_height: Utils.dp(62) - readonly property int history_delegate_spacing: Utils.dp(10) - readonly property color history_delegate_address_color: "#7879b2" - readonly property int button_height: Utils.dp(36) - readonly property bool is_layout_android: true readonly property bool is_layout_ios: false + readonly property string layout: "android" readonly property bool leftNavigation: true readonly property bool bottomNavigation: false } diff --git a/resources/qml/Governikus/Global/+mobile/+android/ConfirmationPopup.qml b/resources/qml/Governikus/Global/+mobile/+android/ConfirmationPopup.qml new file mode 100644 index 0000000..d944c24 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+android/ConfirmationPopup.qml @@ -0,0 +1,44 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + + +BaseConfirmationPopup { + id: root + + buttons: Row { + width: parent.width + + layoutDirection: Qt.RightToLeft + rightPadding: Constants.groupbox_spacing / 2 + bottomPadding: Constants.groupbox_spacing / 2 + spacing: 0 + + GButton { + visible: style & ConfirmationPopup.PopupStyle.OkButton + + buttonColor: Style.color.transparent + text: root.okButtonText + textStyle: Style.text.normal_accent + + onClicked: root.accept() + } + + GButton { + visible: style & ConfirmationPopup.PopupStyle.CancelButton + + buttonColor: Style.color.transparent + text: root.cancelButtonText + textStyle: Style.text.normal_accent + + onClicked: root.cancel() + } + } +} + diff --git a/resources/qml/Governikus/Global/+mobile/+android/GButton.qml b/resources/qml/Governikus/Global/+mobile/+android/GButton.qml index 9784afb..9609343 100644 --- a/resources/qml/Governikus/Global/+mobile/+android/GButton.qml +++ b/resources/qml/Governikus/Global/+mobile/+android/GButton.qml @@ -1,8 +1,16 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 +import Governikus.Style 1.0 + import "Utils.js" as Utils +import Governikus.Style 1.0 + /* * Custom implementation to be replaced with template specialization of Qt.labs.controls Button * Android style guide for material design is adapted. @@ -10,25 +18,29 @@ import "Utils.js" as Utils Item { id: baseItem property alias text: textItem.text - property color buttonColor: Constants.blue + property var textStyle: enabled ? Style.text.button : Style.text.button_disabled + property color buttonColor: Style.color.accent property int maxWidth: 0 property alias iconSource: icon.source property bool animationsDisabled: false signal clicked - height: Constants.button_height - width: Math.max(textItem.implicitWidth + (icon.visible ? (icon.width + icon.anchors.leftMargin) : 0) + (2 * Utils.dp(16)), Utils.dp(88)) + Accessible.role: Accessible.Button + Accessible.name: text + + height: Style.dimens.button_height + width: Math.max(textItem.implicitWidth + (icon.visible ? (icon.width + icon.anchors.leftMargin) : 0) + 2 * 16, 88) state: "normal" states: [ State { name: "normal"; when: baseItem.animationsDisabled || !mouseArea.pressed PropertyChanges { target: darkLayer; width: 0 } - PropertyChanges { target: shadow; verticalOffset: Utils.dp(2) } + PropertyChanges { target: shadow; verticalOffset: 2 } }, State { name: "pressed"; when: !baseItem.animationsDisabled && mouseArea.pressed PropertyChanges { target: darkLayer; width: 2 * rect.width } - PropertyChanges { target: shadow; verticalOffset: Utils.dp(8) } + PropertyChanges { target: shadow; verticalOffset: 8 } } ] transitions: [ @@ -42,8 +54,8 @@ Item { Rectangle { id: rect anchors.fill: parent - color: enabled ? buttonColor : "#10000000" - radius: Utils.dp(3) + color: enabled ? buttonColor : Style.color.accent_disabled + radius: Style.dimens.button_radius Item { anchors.fill: parent @@ -52,9 +64,9 @@ Item { id: darkLayer x: mouseArea.containsMouse ? mouseArea.mouseX - width * 0.5 : 0 height: parent.height - color: "#000000" + color: Constants.black opacity: 0.2 - radius: Utils.dp(3) + radius: Style.dimens.button_radius } } } @@ -71,24 +83,26 @@ Item { Image { id: icon visible: source.toString().length > 0 - height: rect.height - Utils.dp(10) + height: rect.height - 10 width: height anchors.left: rect.left - anchors.leftMargin: Utils.dp(5) + anchors.leftMargin: 5 anchors.verticalCenter: rect.verticalCenter } - Text { + GText { id: textItem + anchors.left: rect.left anchors.right: rect.right anchors.verticalCenter: rect.verticalCenter anchors.leftMargin: icon.visible ? icon.width + icon.anchors.leftMargin : 0 + + Accessible.ignored: true + horizontalAlignment: Text.AlignHCenter - color: enabled ? "white" : "#40000000" font.capitalization: Font.AllUppercase - font.bold: true - font.pixelSize: Utils.dp(16) + textStyle: baseItem.textStyle } MouseArea { diff --git a/resources/qml/Governikus/Global/+mobile/+android/GCheckBox.qml b/resources/qml/Governikus/Global/+mobile/+android/GCheckBox.qml index c57af7b..2091990 100644 --- a/resources/qml/Governikus/Global/+mobile/+android/GCheckBox.qml +++ b/resources/qml/Governikus/Global/+mobile/+android/GCheckBox.qml @@ -1,7 +1,12 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import "Utils.js" as Utils @@ -10,19 +15,19 @@ CheckBox { id: control indicator: Rectangle { - implicitHeight: Utils.dp(20) - implicitWidth: Utils.dp(20) + implicitHeight: 20 + implicitWidth: 20 anchors.centerIn: parent - color: control.checked ? Constants.accent_color : Constants.white - border.color: control.checked ? Constants.accent_color : Constants.black - border.width: Utils.dp(2) - radius: Utils.dp(2) + color: control.checked ? Style.color.accent : Style.color.accent_disabled + border.color: control.checked ? Style.color.accent : Constants.black + border.width: 2 + radius: 2 Image { source: "qrc:///images/check.svg" anchors.fill: parent - anchors.margins: Utils.dp(3) + anchors.margins: 3 fillMode: Image.PreserveAspectFit visible: control.checked } diff --git a/resources/qml/Governikus/Global/+mobile/+android/LabeledText.qml b/resources/qml/Governikus/Global/+mobile/+android/LabeledText.qml index b3317c2..cacdcaa 100644 --- a/resources/qml/Governikus/Global/+mobile/+android/LabeledText.qml +++ b/resources/qml/Governikus/Global/+mobile/+android/LabeledText.qml @@ -1,39 +1,39 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import Governikus.Style 1.0 + Item { property alias label: labelText.text property alias text: bodyText.text property alias textFormat: bodyText.textFormat property int margin - property int fontUppercase signal linkActivated(string link) height: childrenRect.height + margin - Text { + GText { id: bodyText anchors.top: parent.top anchors.left: parent.left anchors.leftMargin: margin anchors.right: parent.right anchors.rightMargin: margin - font.pixelSize: Constants.normal_font_size - font.capitalization: fontUppercase - color: Constants.secondary_text - wrapMode: Text.WordWrap + textStyle: Style.text.normal onLinkActivated: parent.linkActivated(link) } - Text { + GText { id: labelText anchors.top: bodyText.bottom anchors.left: parent.left anchors.leftMargin: margin anchors.right: parent.right anchors.rightMargin: margin - font.pixelSize: Constants.label_font_size - color: Constants.blue - wrapMode: Text.WordWrap + textStyle: Style.text.hint_accent } } diff --git a/resources/qml/Governikus/Global/+mobile/+android/Pane.qml b/resources/qml/Governikus/Global/+mobile/+android/Pane.qml index 9fe7d08..6ec2b27 100644 --- a/resources/qml/Governikus/Global/+mobile/+android/Pane.qml +++ b/resources/qml/Governikus/Global/+mobile/+android/Pane.qml @@ -1,6 +1,12 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 +import Governikus.Style 1.0 + Rectangle { id: root property alias title: titleText.text @@ -9,7 +15,7 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right height: childrenRect.height - color: "white" + color: Style.color.background_pane radius: 16 Column { diff --git a/resources/qml/Governikus/ProviderView/+mobile/+android/SearchBar.qml b/resources/qml/Governikus/Global/+mobile/+android/SearchBar.qml similarity index 75% rename from resources/qml/Governikus/ProviderView/+mobile/+android/SearchBar.qml rename to resources/qml/Governikus/Global/+mobile/+android/SearchBar.qml index 4de7099..b097f53 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+android/SearchBar.qml +++ b/resources/qml/Governikus/Global/+mobile/+android/SearchBar.qml @@ -1,7 +1,12 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 Row { @@ -12,19 +17,21 @@ Row { readonly property alias searchText: searchField.displayText anchors.top: parent ? parent.top : undefined - anchors.topMargin: Constants.titlebar_padding + anchors.topMargin: Style.dimens.titlebar_padding anchors.right: parent ? parent.right : undefined anchors.bottom: parent ? parent.bottom : undefined - anchors.bottomMargin: Constants.titlebar_padding - spacing: Constants.titlebar_padding + anchors.bottomMargin: Style.dimens.titlebar_padding + spacing: Style.dimens.titlebar_padding GTextField { id: searchField height: parent.height - width: root.availableWidth - parent.spacing - iconItem.width - 2 * Constants.titlebar_padding + width: root.availableWidth - parent.spacing - iconItem.width - Style.dimens.titlebar_padding visible: false + enterKeyType: Qt.EnterKeySearch + onAccepted: { iconItem.forceActiveFocus(Qt.MouseFocusReason) } @@ -42,7 +49,7 @@ Row { height: parent.height width: height fillMode: Image.PreserveAspectFit - source: "qrc:///images/search.svg" + source: "qrc:///images/android/search_icon.svg" MouseArea { anchors.fill: parent @@ -56,7 +63,7 @@ Row { searchField.forceActiveFocus(Qt.MouseFocusReason) Qt.inputMethod.show() } else { - iconItem.source = "qrc:///images/search.svg" + iconItem.source = "qrc:///images/android/search_icon.svg" iconItem.forceActiveFocus(Qt.MouseFocusReason) searchField.text = "" Qt.inputMethod.hide() diff --git a/resources/qml/Governikus/Global/+mobile/+ios/+phone/DeviceConstants.qml b/resources/qml/Governikus/Global/+mobile/+ios/+phone/DeviceConstants.qml index 59348c1..7c0ed0a 100644 --- a/resources/qml/Governikus/Global/+mobile/+ios/+phone/DeviceConstants.qml +++ b/resources/qml/Governikus/Global/+mobile/+ios/+phone/DeviceConstants.qml @@ -1,9 +1,9 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 -import "Utils.js" as Utils - Item { - readonly property int history_section_height: Utils.dp(85) - readonly property bool is_tablet: false } diff --git a/resources/qml/Governikus/Global/+mobile/+ios/+phone/Pane.qml b/resources/qml/Governikus/Global/+mobile/+ios/+phone/Pane.qml index 091b234..09ebb79 100644 --- a/resources/qml/Governikus/Global/+mobile/+ios/+phone/Pane.qml +++ b/resources/qml/Governikus/Global/+mobile/+ios/+phone/Pane.qml @@ -1,43 +1,43 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import Governikus.Style 1.0 + import "Utils.js" as Utils Column { id: root property alias title: titleText.text - property alias spacing: paneContent.spacing + property alias contentSpacing: paneContent.spacing default property alias paneData: paneContent.data anchors.left: parent.left anchors.right: parent.right + spacing: Constants.groupbox_spacing + PaneTitle { id: titleText - height: Utils.dp(30) anchors.margins: Constants.pane_padding font.capitalization: Font.AllUppercase - color: Constants.grey } - Rectangle { width: parent.width; height: 1; color: Constants.grey} Rectangle { - color: "white" + color: Style.color.background_pane width: parent.width - height: childrenRect.height + height: paneContent.height + 2 * Constants.pane_padding + radius: Style.dimens.corner_radius Column { - width: parent.width - Item { width: parent.width; height: Constants.pane_padding } - Column { - id: paneContent - anchors.left: parent.left - anchors.leftMargin: Constants.pane_padding - anchors.right: parent.right - anchors.rightMargin: Constants.pane_padding - spacing: Constants.component_spacing - } - Item { width: parent.width; height: Constants.pane_padding } + id: paneContent + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Constants.pane_padding + spacing: Constants.component_spacing } } - Rectangle { width: parent.width; height: 1; color: Constants.grey} } diff --git a/resources/qml/Governikus/Global/+mobile/+ios/+tablet/DeviceConstants.qml b/resources/qml/Governikus/Global/+mobile/+ios/+tablet/DeviceConstants.qml index 91e4b85..4afbd01 100644 --- a/resources/qml/Governikus/Global/+mobile/+ios/+tablet/DeviceConstants.qml +++ b/resources/qml/Governikus/Global/+mobile/+ios/+tablet/DeviceConstants.qml @@ -1,9 +1,9 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 -import "Utils.js" as Utils - Item { - readonly property int history_section_height: Utils.dp(60) - readonly property bool is_tablet: true } diff --git a/resources/qml/Governikus/Global/+mobile/+ios/+tablet/Pane.qml b/resources/qml/Governikus/Global/+mobile/+ios/+tablet/Pane.qml index 4919635..e848b11 100644 --- a/resources/qml/Governikus/Global/+mobile/+ios/+tablet/Pane.qml +++ b/resources/qml/Governikus/Global/+mobile/+ios/+tablet/Pane.qml @@ -1,6 +1,11 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 +import Governikus.Style 1.0 Rectangle { id: root @@ -10,35 +15,30 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right - height: childrenRect.height - color: "white" + height: content.implicitHeight + 2 * Constants.pane_padding + color: Style.color.background_pane + radius: Style.dimens.corner_radius Column { - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Constants.pane_padding - anchors.rightMargin: Constants.pane_padding + id: content - Text { + anchors { + fill: parent + margins: Constants.pane_padding + } + + spacing: Constants.pane_padding + + PaneTitle { id: titleText height: implicitHeight * 2 verticalAlignment: Text.AlignVCenter } - Item { width: parent.width; height: Constants.pane_padding } + Column { id: paneContent width: parent.width spacing: Constants.pane_spacing } - Item { width: parent.width; height: Constants.pane_padding } - } - - layer.enabled: true - layer.effect: DropShadow { - radius: 8 - samples: 8 - source: root - color: Constants.grey - verticalOffset: 2 } } diff --git a/resources/qml/Governikus/Global/+mobile/+ios/BrandConstants.qml b/resources/qml/Governikus/Global/+mobile/+ios/BrandConstants.qml index 83e0a90..0aef4b2 100644 --- a/resources/qml/Governikus/Global/+mobile/+ios/BrandConstants.qml +++ b/resources/qml/Governikus/Global/+mobile/+ios/BrandConstants.qml @@ -1,24 +1,13 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 -import "Utils.js" as Utils - DeviceConstants { - readonly property color grey_light: "#8e8e93" - readonly property color blue_dark: "#0076ff" - readonly property color blue_light: "#54c7fc" - readonly property color accent_color: "#000000" - readonly property color secondary_text: "#000000" - readonly property int titlebar_font_size: Utils.dp(16) - readonly property int provider_section_height: Utils.dp(50) - readonly property int history_delegate_spacing: 0 - readonly property color history_delegate_address_color: "#00878F" - readonly property int button_height: Utils.dp(40) - readonly property bool is_layout_android: false readonly property bool is_layout_ios: true + readonly property string layout: "ios" readonly property bool leftNavigation: false readonly property bool bottomNavigation: true - - readonly property int searchbar_height: Utils.dp(48) - readonly property int tabbar_height: Utils.dp(48) } diff --git a/resources/qml/Governikus/Global/+mobile/+ios/ConfirmationPopup.qml b/resources/qml/Governikus/Global/+mobile/+ios/ConfirmationPopup.qml new file mode 100644 index 0000000..216ff28 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/+ios/ConfirmationPopup.qml @@ -0,0 +1,65 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + + +BaseConfirmationPopup { + id: root + + horizontalTextAlignment: Text.AlignHCenter + + buttons: ColumnLayout { + width: parent.width + + spacing: 0 + + GSeparator { + Layout.fillWidth: true + visible: style !== ConfirmationPopup.PopupStyle.NoButtons + } + + RowLayout { + Layout.fillWidth: true + + spacing: 0 + + GButton { + visible: style & ConfirmationPopup.PopupStyle.CancelButton + + Layout.fillWidth: true + + buttonColor: Style.color.transparent + text: root.cancelButtonText + textStyle: Style.text.normal_accent + + onClicked: root.cancel() + } + + GSeparator { + visible: style & ConfirmationPopup.PopupStyle.CancelButton && style & ConfirmationPopup.PopupStyle.OkButton + Layout.fillHeight: true + orientation: Qt.Vertical + } + + GButton { + visible: style & ConfirmationPopup.PopupStyle.OkButton + + Layout.fillWidth: true + + buttonColor: Style.color.transparent + text: root.okButtonText + textStyle: Style.text.normal_accent + + onClicked: root.accept() + } + } + } +} + diff --git a/resources/qml/Governikus/Global/+mobile/+ios/GButton.qml b/resources/qml/Governikus/Global/+mobile/+ios/GButton.qml index c484da6..f231083 100644 --- a/resources/qml/Governikus/Global/+mobile/+ios/GButton.qml +++ b/resources/qml/Governikus/Global/+mobile/+ios/GButton.qml @@ -1,46 +1,60 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 import "Utils.js" as Utils +import Governikus.Style 1.0 + /* Custom implementation to be replaced with template specialization of Qt.labs.controls Button*/ Rectangle { id: rect property alias text: textItem.text - property color buttonColor : Constants.blue + property var textStyle: enabled ? Style.text.button : Style.text.button_disabled + property color buttonColor : Style.color.accent property int maxWidth: 0 - property int preferedWidth: Math.max(textItem.implicitWidth + (icon.visible ? (icon.width + icon.anchors.leftMargin) : 0) + (2 * Utils.dp(16)), Utils.dp(88)) + property int preferedWidth: Math.max(textItem.implicitWidth + (icon.visible ? (icon.width + icon.anchors.leftMargin) : 0) + (2 * 16), 88) property alias iconSource: icon.source property bool animationsDisabled: false signal clicked - color: enabled ? buttonColor : "#10000000" - height: Constants.button_height + Accessible.role: Accessible.Button + Accessible.name: text + Accessible.onPressAction: clicked() + + color: enabled ? buttonColor : Style.color.accent_disabled + height: Style.dimens.button_height width: maxWidth > 0 ? Math.min(maxWidth, preferedWidth) : preferedWidth clip: true + radius: Style.dimens.button_radius Image { id: icon visible: source.toString().length > 0 - height: rect.height - Utils.dp(10) + height: rect.height - 10 width: height anchors.left: rect.left - anchors.leftMargin: Utils.dp(5) + anchors.leftMargin: 5 anchors.verticalCenter: rect.verticalCenter } - Text { + GText { id: textItem + anchors.left: rect.left anchors.right: rect.right anchors.verticalCenter: rect.verticalCenter - color: enabled ? "white" : "#40000000" horizontalAlignment: Text.AlignHCenter + + Accessible.ignored: true + opacity: mouseArea.containsMouse ? 0.5 : 1 anchors.leftMargin: icon.visible ? icon.width + icon.anchors.leftMargin : 0 - font.pixelSize: Utils.dp(16) - font.bold: true + textStyle: rect.textStyle } MouseArea{ diff --git a/resources/qml/Governikus/Global/+mobile/+ios/GCheckBox.qml b/resources/qml/Governikus/Global/+mobile/+ios/GCheckBox.qml index c098ea7..c6ea829 100644 --- a/resources/qml/Governikus/Global/+mobile/+ios/GCheckBox.qml +++ b/resources/qml/Governikus/Global/+mobile/+ios/GCheckBox.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 @@ -9,19 +13,25 @@ import "Utils.js" as Utils CheckBox { id: control + Accessible.role: Accessible.CheckBox + Accessible.checkable: true + Accessible.checked: checked + Accessible.name: text + Accessible.onPressAction: toggle() + indicator: Rectangle { - implicitHeight: Utils.dp(26) - implicitWidth: Utils.dp(26) + implicitHeight: 26 + implicitWidth: 26 anchors.centerIn: parent color: Constants.white border.color: Constants.black - border.width: Utils.dp(1) + border.width: 1 Image { source: "qrc:///images/iOS/CheckedCheckbox.png" anchors.fill: parent - anchors.margins: Utils.dp(2) + anchors.margins: 2 fillMode: Image.PreserveAspectFit visible: control.checked } diff --git a/resources/qml/Governikus/Global/+mobile/+ios/LabeledText.qml b/resources/qml/Governikus/Global/+mobile/+ios/LabeledText.qml index a5e05f0..10094c5 100644 --- a/resources/qml/Governikus/Global/+mobile/+ios/LabeledText.qml +++ b/resources/qml/Governikus/Global/+mobile/+ios/LabeledText.qml @@ -1,40 +1,49 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import Governikus.Style 1.0 Item { property alias label: labelText.text property alias text: bodyText.text property alias textFormat: bodyText.textFormat property int margin - property int fontUppercase signal linkActivated(string link) height: childrenRect.height + margin - Text { + Accessible.role: Accessible.StaticText + Accessible.name: label + ": " + text + + GText { id: labelText + + Accessible.ignored: true + anchors.top: parent.top anchors.left: parent.left anchors.leftMargin: margin anchors.right: parent.right anchors.rightMargin: margin - font.pixelSize: Constants.normal_font_size - color: Constants.blue - wrapMode: Text.WordWrap + + textStyle: Style.text.normal_accent } - Text { + GText { id: bodyText - color: Constants.secondary_text + + Accessible.ignored: true + anchors.top: labelText.bottom anchors.left: parent.left anchors.leftMargin: margin anchors.right: parent.right anchors.rightMargin: margin - font.pixelSize: Constants.normal_font_size - font.capitalization: fontUppercase - wrapMode: Text.WordWrap + textStyle: Style.text.normal_secondary onLinkActivated: parent.linkActivated(link) } } diff --git a/resources/qml/Governikus/ProviderView/+mobile/+ios/SearchBar.qml b/resources/qml/Governikus/Global/+mobile/+ios/SearchBar.qml similarity index 61% rename from resources/qml/Governikus/ProviderView/+mobile/+ios/SearchBar.qml rename to resources/qml/Governikus/Global/+mobile/+ios/SearchBar.qml index 05a454a..ca34423 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+ios/SearchBar.qml +++ b/resources/qml/Governikus/Global/+mobile/+ios/SearchBar.qml @@ -1,20 +1,29 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 Item { readonly property alias searchText: searchField.text id: baseItem - height: Constants.searchbar_height + height: Style.dimens.searchbar_height + + Accessible.role: Accessible.EditableText + Accessible.description: qsTr("Enter search string") + SettingsModel.translationTrigger MouseArea { id: pageArea onClicked: pageArea.focus = true - height: Constants.searchbar_height + height: Style.dimens.searchbar_height width: parent.width anchors.bottom: parent.bottom @@ -23,31 +32,38 @@ Item { anchors.right: cancelButton.left anchors.top: parent.top anchors.bottom: parent.bottom - anchors.leftMargin: Utils.dp(8) - anchors.rightMargin: Utils.dp(10) - anchors.topMargin: Utils.dp(6) - anchors.bottomMargin: Utils.dp(8) - radius: Utils.dp(6) - color: "white" + anchors.leftMargin: 8 + anchors.rightMargin: 10 + anchors.topMargin: 6 + anchors.bottomMargin: 8 + radius: 6 + color: Constants.white Image { id: glassIcon height: parent.height - 2 * anchors.leftMargin width: height anchors.left: parent.left - anchors.leftMargin: Utils.dp(4) + anchors.leftMargin: 4 anchors.verticalCenter: parent.verticalCenter + sourceSize.width: width source: "qrc:///images/iOS/search_icon.svg" } TextField { id: searchField - anchors.margins: Utils.dp(8) + + anchors.margins: 8 anchors.verticalCenter: parent.verticalCenter anchors.left: glassIcon.right anchors.right: textEditX.left + + Accessible.role: Accessible.EditableText + Accessible.name: displayText + horizontalAlignment: Text.AlignLeft background: Item {} + EnterKey.type: Qt.EnterKeySearch onVisibleChanged: { if (visible === false){ @@ -58,49 +74,55 @@ Item { Label { id: searchLabel - anchors.left: searchField.left - anchors.leftMargin: Utils.dp(8) - anchors.verticalCenter: searchField.verticalCenter - text: qsTr("Search") + settingsModel.translationTrigger - color: Constants.grey - font.pixelSize: Constants.normal_font_size + visible: searchField.text.trim().length === 0 + anchors.left: searchField.left + anchors.leftMargin: 8 + anchors.verticalCenter: searchField.verticalCenter + + Accessible.ignored: true + + //: LABEL IOS + text: qsTr("Search") + SettingsModel.translationTrigger + color: Constants.grey + font.pixelSize: Style.dimens.normal_font_size } MouseArea { id: textEditX + + visible: searchField.text.length > 0 height: parent.height width: height anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - enabled: searchField.text.length > 0 - onClicked: { - searchField.text = "" - } + Accessible.role: Accessible.Button + Accessible.name: qsTr("Clear search string") + SettingsModel.translationTrigger + Accessible.onPressAction: textEditX.clicked(null) + Accessible.ignored: !visible + + onClicked: searchField.text = "" Image { - anchors.margins: Utils.dp(4) + anchors.margins: 4 anchors.fill: parent source: "qrc:///images/iOS/search_cancel.svg" - visible: parent.enabled } } } - DimmableTextButton { + GButton { id: cancelButton - anchors.right: parent.right - anchors.rightMargin: visible ? Utils.dp(8) : 0 - anchors.verticalCenter: parent.verticalCenter - clip: true visible: false - width: visible ? cancelButton.implicitWidth : 0 - font.family: "Helvetica" - font.pixelSize: Constants.normal_font_size - color: "white" - text: qsTr("Cancel") + settingsModel.translationTrigger + anchors.right: parent.right + anchors.rightMargin: visible ? 8 : 0 + anchors.verticalCenter: parent.verticalCenter + + width: visible ? preferedWidth : 0 + //: LABEL IOS + text: qsTr("Cancel") + SettingsModel.translationTrigger onClicked: { searchField.text = "" pageArea.clicked(null) diff --git a/resources/qml/Governikus/Global/+mobile/ConfirmationPopup.qml b/resources/qml/Governikus/Global/+mobile/ConfirmationPopup.qml deleted file mode 100644 index fb9369a..0000000 --- a/resources/qml/Governikus/Global/+mobile/ConfirmationPopup.qml +++ /dev/null @@ -1,87 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 - -Popup { - id: popupObject - signal confirmed - - property var baseItem: Item { } - x: (baseItem.width - width) / 2 - y: (baseItem.height - height) / 2 - - property alias title: header.text - property alias text: info.text - property alias confirmText: textItemDelete.text - - modal: true - focus: true - closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape - width: Utils.dp(250) - height: popupColumn.height - - Column { - id: popupColumn - width: parent.width - spacing: Constants.pane_spacing - padding: Constants.pane_padding - - Text { - id: header - color: Constants.secondary_text - font.pixelSize: Constants.header_font_size - font.bold: true - } - - Text { - id: info - color: Constants.secondary_text - width: parent.width - 2 * Constants.pane_padding - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - } - - Row { - id: buttonBox - anchors.right: parent.right - anchors.rightMargin: Constants.pane_padding - spacing: Constants.pane_spacing - - MouseArea { - height: textItemCancel.height + 2 * Utils.dp(10) - width: textItemCancel.width + 2 * Utils.dp(10) - - Text { - id: textItemCancel - anchors.centerIn: parent - text: qsTr("Cancel") + settingsModel.translationTrigger - color: Constants.blue - font.pixelSize: Constants.titlebar_font_size - } - - onClicked: { - popupObject.close() - } - } - - MouseArea { - height: textItemDelete.height + 2 * Utils.dp(10) - width: textItemDelete.width + 2 * Utils.dp(10) - - Text { - id: textItemDelete - anchors.centerIn: parent - color: Constants.blue - font.pixelSize: Constants.titlebar_font_size - } - - onClicked: { - popupObject.confirmed() - popupObject.close() - } - } - } - } -} - diff --git a/resources/qml/Governikus/Global/+mobile/GComboBox.qml b/resources/qml/Governikus/Global/+mobile/GComboBox.qml index bfb3fd6..688560a 100644 --- a/resources/qml/Governikus/Global/+mobile/GComboBox.qml +++ b/resources/qml/Governikus/Global/+mobile/GComboBox.qml @@ -1,28 +1,33 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import "Utils.js" as Utils +import Governikus.Style 1.0 + ComboBox { id: control - spacing: Constants.groupbox_spacing - font.pixelSize: Constants.normal_font_size - QtObject { - id: d - readonly property var mainColor: Constants.black - readonly property var pressedColor: Constants.black - } + property var textStyle: Style.text.normal + + Accessible.role: Accessible.ComboBox + Accessible.name: displayText + + spacing: Constants.groupbox_spacing + font.pixelSize: textStyle.fontSize onPressedChanged: canvas.requestPaint() Component.onCompleted: canvas.requestPaint() delegate: ItemDelegate { width: control.width - contentItem: Text { + contentItem: GText { text: modelData - color: d.mainColor - font: control.font + textStyle: control.textStyle elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } @@ -33,8 +38,8 @@ ComboBox { id: canvas x: control.width - width - control.rightPadding y: control.topPadding + (control.availableHeight - height) / 2 - width: Utils.dp(12) - height: Utils.dp(8) + width: 12 + height: 8 contextType: "2d" onPaint: { @@ -43,33 +48,32 @@ ComboBox { context.lineTo(width, 0); context.lineTo(width / 2, height); context.closePath(); - context.fillStyle = control.pressed ? d.pressedColor : d.mainColor; + context.fillStyle = Constants.black context.fill(); } } - contentItem: Text { + contentItem: GText { padding: control.spacing rightPadding: control.indicator.width + control.spacing text: control.displayText - font: control.font - color: control.pressed ? d.pressedColor : d.mainColor + textStyle: control.textStyle verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } background: Rectangle { - border.color: control.pressed ? d.pressedColor : d.mainColor - border.width: control.visualFocus ? Utils.dp(2) : Utils.dp(1) - radius: Utils.dp(2) + border.color: Constants.black + border.width: control.visualFocus ? 2 : 1 + radius: 2 } popup: Popup { y: control.height - 1 width: control.width implicitHeight: contentItem.implicitHeight - padding: Utils.dp(1) + padding: 1 contentItem: ListView { clip: true @@ -81,8 +85,8 @@ ComboBox { } background: Rectangle { - border.color: d.mainColor - radius: Utils.dp(2) + border.color: Constants.black + radius: 2 } } } diff --git a/resources/qml/Governikus/Global/+mobile/GFlickable.qml b/resources/qml/Governikus/Global/+mobile/GFlickable.qml new file mode 100644 index 0000000..d5f3570 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/GFlickable.qml @@ -0,0 +1,51 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.10 + +import Governikus.Style 1.0 + +Flickable { + id: baseItem + + property bool scrollBarEnabled: true + property real scrollBarTopPadding: 0 + property real scrollBarBottomPadding: 0 + + function scrollPageDown() { + if (contentHeight > height) { + Utils.scrollPageDown(baseItem) + } + } + + function scrollPageUp() { + if (contentHeight > height) { + Utils.scrollPageUp(baseItem) + } + } + + function highlightScrollbar() { + if (ScrollBar.vertical) ScrollBar.vertical.highlight() + } + + ScrollBar.vertical: scrollBarEnabled ? scrollBar.createObject() : null + + maximumFlickVelocity: Constants.scrolling_speed + flickableDirection: Flickable.VerticalFlick + + boundsMovement: Flickable.FollowBoundsBehavior + boundsBehavior: contentHeight <= height ? Flickable.StopAtBounds : Flickable.DragAndOvershootBounds + + onVisibleChanged: if (visible) highlightScrollbar() + + Component { + id: scrollBar + GScrollBar { + topPadding: baseItem.scrollBarTopPadding + Style.dimens.scrollbar_padding + bottomPadding: baseItem.scrollBarBottomPadding + Style.dimens.scrollbar_padding + } + } +} diff --git a/resources/qml/Governikus/Global/+mobile/GGridView.qml b/resources/qml/Governikus/Global/+mobile/GGridView.qml new file mode 100644 index 0000000..991edb5 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/GGridView.qml @@ -0,0 +1,51 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.10 + +import Governikus.Style 1.0 + +GridView { + id: baseItem + + property bool scrollBarEnabled: true + property real scrollBarTopPadding: 0 + property real scrollBarBottomPadding: 0 + + function scrollPageDown() { + if (contentHeight > height) { + Utils.scrollPageDown(baseItem) + } + } + + function scrollPageUp() { + if (contentHeight > height) { + Utils.scrollPageUp(baseItem) + } + } + + function highlightScrollbar() { + if (ScrollBar.vertical) ScrollBar.vertical.highlight() + } + + ScrollBar.vertical: scrollBarEnabled ? scrollBar.createObject() : null + + maximumFlickVelocity: Constants.scrolling_speed + flickableDirection: Flickable.VerticalFlick + + boundsMovement: Flickable.FollowBoundsBehavior + boundsBehavior: contentHeight <= height ? Flickable.StopAtBounds : Flickable.DragAndOvershootBounds + + onVisibleChanged: if (visible) highlightScrollbar() + + Component { + id: scrollBar + GScrollBar { + topPadding: baseItem.scrollBarTopPadding + Style.dimens.scrollbar_padding + bottomPadding: baseItem.scrollBarBottomPadding + Style.dimens.scrollbar_padding + } + } +} diff --git a/resources/qml/Governikus/Global/+mobile/GListView.qml b/resources/qml/Governikus/Global/+mobile/GListView.qml new file mode 100644 index 0000000..ca063fd --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/GListView.qml @@ -0,0 +1,51 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.10 + +import Governikus.Style 1.0 + +ListView { + id: baseItem + + property bool scrollBarEnabled: true + property real scrollBarTopPadding: 0 + property real scrollBarBottomPadding: 0 + + function scrollPageDown() { + if (contentHeight > height) { + Utils.scrollPageDown(baseItem) + } + } + + function scrollPageUp() { + if (contentHeight > height) { + Utils.scrollPageUp(baseItem) + } + } + + function highlightScrollbar() { + if (ScrollBar.vertical) ScrollBar.vertical.highlight() + } + + ScrollBar.vertical: scrollBarEnabled ? scrollBar.createObject() : null + + maximumFlickVelocity: Constants.scrolling_speed + flickableDirection: Flickable.VerticalFlick + + boundsMovement: Flickable.FollowBoundsBehavior + boundsBehavior: contentHeight <= height ? Flickable.StopAtBounds : Flickable.DragAndOvershootBounds + + onVisibleChanged: if (visible) highlightScrollbar() + + Component { + id: scrollBar + GScrollBar { + topPadding: baseItem.scrollBarTopPadding + Style.dimens.scrollbar_padding + bottomPadding: baseItem.scrollBarBottomPadding + Style.dimens.scrollbar_padding + } + } +} diff --git a/resources/qml/Governikus/Global/+mobile/GRadioButton.qml b/resources/qml/Governikus/Global/+mobile/GRadioButton.qml index f806df0..0d20557 100644 --- a/resources/qml/Governikus/Global/+mobile/GRadioButton.qml +++ b/resources/qml/Governikus/Global/+mobile/GRadioButton.qml @@ -1,14 +1,25 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import "Utils.js" as Utils +import Governikus.Style 1.0 + RadioButton { id: control + + property var textStyle: Style.text.normal + + Accessible.name: text + spacing: Constants.groupbox_spacing indicator: Rectangle { - implicitWidth: Utils.dp(26) + implicitWidth: 26 implicitHeight: implicitWidth x: control.leftPadding y: parent.height / 2 - height / 2 @@ -16,7 +27,7 @@ RadioButton { border.color: Qt.darker(Constants.blue, control.down ? 1.5 : 1) Rectangle { - width: Utils.dp(14) + width: 14 height: width x: (parent.width - width) / 2 y: x @@ -26,10 +37,11 @@ RadioButton { } } - contentItem: Text { + contentItem: GText { + Accessible.ignored: true + text: control.text - font.pixelSize: Constants.label_font_size - color: Constants.secondary_text + textStyle: control.textStyle verticalAlignment: Text.AlignVCenter leftPadding: control.indicator.width + control.spacing } diff --git a/resources/qml/Governikus/Global/+mobile/GScrollBar.qml b/resources/qml/Governikus/Global/+mobile/GScrollBar.qml new file mode 100644 index 0000000..e9ab49d --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/GScrollBar.qml @@ -0,0 +1,45 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Style 1.0 + +ScrollBar { + id: baseItem + + function highlight() { + highlighted = true + highlightTimer.restart() + } + + property bool highlighted: false + + // Using only ScrollBar.AsNeeded leads to the scrollbar becoming visible when highlighted + policy: size < 1.0 ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff + minimumSize: Style.dimens.minimumScrollBarSize + + contentItem: Rectangle { + implicitWidth: 3 + implicitHeight: 100 + radius: width / 2 + color: baseItem.pressed ? Style.color.accent : Constants.grey + opacity: (active || highlighted) ? 1.0 : 0.0 + + Behavior on opacity { + NumberAnimation { + duration: Constants.animation_duration + easing.type: Easing.InOutCubic + } + } + } + + onPolicyChanged: if (policy == ScrollBar.AlwaysOn) highlight() + + Timer { + id: highlightTimer + onTriggered: baseItem.highlighted = false + } +} diff --git a/resources/qml/Governikus/Global/+mobile/GSwitch.qml b/resources/qml/Governikus/Global/+mobile/GSwitch.qml index 2f0c793..4253010 100644 --- a/resources/qml/Governikus/Global/+mobile/GSwitch.qml +++ b/resources/qml/Governikus/Global/+mobile/GSwitch.qml @@ -1,17 +1,30 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import "Utils.js" as Utils +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + MouseArea { id: baseItem property bool initialState: false - property color color: Constants.blue + property color color: Style.color.accent readonly property alias isOn: toggleswitch.isOn signal switched() - width: Utils.dp(60) - height: Utils.dp(48) + width: 60 + height: 48 + + Accessible.role: Accessible.CheckBox + Accessible.name: qsTr("Switch") + SettingsModel.translationTrigger + Accessible.checkable: true + Accessible.checked: isOn + Accessible.onPressAction: if (Qt.platform.os === "ios") clicked(null) onClicked: toggleswitch.toggle() Component.onCompleted: toggleswitch.isOn = baseItem.initialState @@ -19,8 +32,8 @@ MouseArea { Item { id: toggleswitch anchors.fill: parent - anchors.topMargin: Utils.dp(12) - anchors.bottomMargin: Utils.dp(12) + anchors.topMargin: 12 + anchors.bottomMargin: 12 onIsOnChanged: updateState() property bool isOn: false @@ -49,7 +62,7 @@ MouseArea { anchors.fill: parent anchors.margins: parent.height / 4 radius: height / 2 - color: isOn ? Qt.lighter(baseItem.color, 1.55) : "lightgray" + color: isOn ? Qt.lighter(baseItem.color, 1.4) : Qt.darker(Style.color.accent_disabled, 1.1) } Rectangle { @@ -57,7 +70,7 @@ MouseArea { height: parent.height width: height radius: width - color: isOn ? baseItem.color : "darkgray" + color: isOn ? baseItem.color : Style.color.accent_disabled MouseArea { anchors.fill: parent diff --git a/resources/qml/Governikus/Global/+mobile/GTextField.qml b/resources/qml/Governikus/Global/+mobile/GTextField.qml index eb90814..214d06a 100644 --- a/resources/qml/Governikus/Global/+mobile/GTextField.qml +++ b/resources/qml/Governikus/Global/+mobile/GTextField.qml @@ -1,36 +1,48 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 +import Governikus.Style 1.0 + import "Utils.js" as Utils Item { property alias text: field.text property alias displayText: field.displayText property alias echoMode: field.echoMode + property int enterKeyType: Qt.EnterKeyDefault signal accepted property bool valid: true - height: Utils.dp(24) - width: Utils.dp(240) + height: 24 + width: 240 + + Accessible.role: Accessible.EditableText + Accessible.editable: true + Accessible.name: echoMode === TextInput.Normal ? displayText : "" Rectangle { - radius: Utils.dp(6) + radius: 6 anchors.fill: parent border.color: Constants.red - color: enabled ? "white" : Constants.grey - border.width: valid ? 0 : Utils.dp(2) + color: enabled ? Constants.white : Constants.grey + border.width: valid ? 0 : 2 } TextField { id: field anchors.fill: parent - anchors.leftMargin: Utils.dp(6) - anchors.rightMargin: Utils.dp(6) + anchors.leftMargin: 6 + anchors.rightMargin: 6 padding: 0 - font.pixelSize: Constants.normal_font_size + font.pixelSize: Style.dimens.normal_font_size onAccepted: parent.accepted() background: Item {} + EnterKey.type: enterKeyType } onActiveFocusChanged: if (focus) field.forceActiveFocus() diff --git a/resources/qml/Governikus/Global/+mobile/ListItem.qml b/resources/qml/Governikus/Global/+mobile/ListItem.qml new file mode 100644 index 0000000..6b6609f --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/ListItem.qml @@ -0,0 +1,139 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + +Rectangle { + id: baseItem + + property alias icon: imageItem.source + property alias headerText: headerItem.text + property alias text: textItem.text + property alias footerText: footerItem.text + + property real contentMarginLeft: Constants.groupbox_spacing + property real contentMarginRight: Constants.groupbox_spacing + + property bool showRightArrow: Constants.is_layout_ios + property bool showSeparator: true + property bool pressed: mouseArea.pressed + property alias mouseAreaEnabled: mouseArea.enabled + + signal clicked + + width: parent.width + height: Style.dimens.list_item_height + + Accessible.role: Accessible.ListItem + Accessible.name: headerText + ". " + text + ". " + footerText + Accessible.onPressAction: if (Qt.platform.os === "ios") mouseArea.clicked(null) + + color: pressed ? Constants.lightgrey : Style.color.background_pane + + GSeparator { + visible: showSeparator + + width: Constants.is_layout_ios ? (parent.width - textLayout.x - contentMarginLeft) : parent.width + anchors.top: parent.bottom + anchors.topMargin: -height + anchors.right: parent.right + } + + RowLayout { + id: content + + anchors.fill: parent + anchors.leftMargin: contentMarginLeft + anchors.rightMargin: contentMarginRight + + spacing: Constants.groupbox_spacing + + Image { + id: imageItem + + visible: baseItem.icon !== "" + sourceSize.height: parent.height - 2 * Constants.groupbox_spacing + + asynchronous: true + fillMode: Image.PreserveAspectFit + } + + ColumnLayout { + id: textLayout + + Layout.fillWidth: true + + spacing: 0 + + GText { + id: headerItem + + visible: baseItem.headerText !== "" + Layout.fillWidth: true + + Accessible.ignored: true + + elide: Text.ElideRight + textStyle: Style.text.hint_accent + maximumLineCount: 1 + } + + GText { + id: textItem + + visible: baseItem.text !== "" + Layout.fillWidth: true + + Accessible.ignored: true + + elide: Text.ElideRight + textStyle: Style.text.normal + maximumLineCount: 2 + } + + GText { + id: footerItem + + visible: baseItem.footerText !== "" + Layout.fillWidth: true + + Accessible.ignored: true + + elide: Text.ElideRight + textStyle: Style.text.hint_secondary + maximumLineCount: 1 + } + } + + Image { + id: arrowRight + + visible: showRightArrow + + sourceSize.height: Style.dimens.small_icon_size + fillMode: Image.PreserveAspectFit + source: "qrc:///images/arrowRight.svg" + + ColorOverlay { + anchors.fill: arrowRight + source: arrowRight + color: Style.color.secondary_text + } + } + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + + onClicked: baseItem.clicked() + } +} diff --git a/resources/qml/Governikus/Global/+mobile/LocationButton.qml b/resources/qml/Governikus/Global/+mobile/LocationButton.qml index ff9806f..9170375 100644 --- a/resources/qml/Governikus/Global/+mobile/LocationButton.qml +++ b/resources/qml/Governikus/Global/+mobile/LocationButton.qml @@ -1,48 +1,64 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + import "Utils.js" as Utils + MouseArea { property string language property string name property string image - height: Utils.dp(35) - width: height - - onClicked: { - settingsModel.language = language + function setLanguage() { + SettingsModel.language = language if (typeof(navigationController) !== "undefined") { navigationController.close() } } + height: 48 + width: height + + Accessible.role: Accessible.Button + Accessible.onPressAction: if (Qt.platform.os === "ios") clicked(null) + onClicked: setLanguage() + Rectangle { opacity: 0.1 border.color: "black" - border.width: settingsModel.language === language ? 0 : Utils.dp(1) - color: settingsModel.language === language ? "black" : Constants.background_color + border.width: SettingsModel.language === language ? 0 : 1 + color: SettingsModel.language === language ? Constants.black : Style.color.accent anchors.fill: parent - radius: Utils.dp(3) + radius: Style.dimens.button_radius } - Text { - text: name - color: Constants.secondary_text + GText { + id: nameText - anchors.margins: Utils.dp(2) + anchors.topMargin: 4 anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: Constants.small_font_size + + Accessible.ignored: true + + text: name + textStyle: Style.text.hint_secondary } Image { source: image fillMode: Image.PreserveAspectFit - anchors.margins: Utils.dp(4) + anchors.margins: 4 anchors.left: parent.left anchors.bottom: parent.bottom anchors.right: parent.right + anchors.top: nameText.bottom } } diff --git a/resources/qml/Governikus/Global/+mobile/PaneTitle.qml b/resources/qml/Governikus/Global/+mobile/PaneTitle.qml new file mode 100644 index 0000000..35e3278 --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/PaneTitle.qml @@ -0,0 +1,19 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Style 1.0 + +GText { + visible: text !== "" + anchors.left: parent.left + anchors.right: parent.right + + Accessible.role: Accessible.TitleBar + Accessible.name: text + + textStyle: Style.text.header_accent + elide: Text.ElideRight +} diff --git a/resources/qml/Governikus/Global/+mobile/PlatformConstants.qml b/resources/qml/Governikus/Global/+mobile/PlatformConstants.qml index 56c6f8b..2ef23f8 100644 --- a/resources/qml/Governikus/Global/+mobile/PlatformConstants.qml +++ b/resources/qml/Governikus/Global/+mobile/PlatformConstants.qml @@ -1,49 +1,16 @@ -import QtQuick 2.10 -import QtQuick.Window 2.2 +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ -import "Utils.js" as Utils +import QtQuick 2.10 BrandConstants { readonly property color blue: "#659bcd" - readonly property color background_color: "#dcebf6" - readonly property color grey_border: "lightslategrey" - readonly property color tutorial_orange: "#f9a501" - readonly property color tutorial_green: "#73d7b3" - readonly property color tutorial_blue: "#659bcd" - readonly property color tutorial_red: "#fb7a59" - readonly property color tutorial_very_light_grey: "#f2f2f2" - - readonly property int header_font_size: thresholdReduce(22) - readonly property int pane_title_font_size: header_font_size - readonly property int normal_font_size: thresholdReduce(16) - readonly property int label_font_size: Utils.dp(14) - readonly property int small_font_size: Utils.dp(12) - - readonly property int tutorial_component_spacing: Utils.dp(40) - readonly property int tutorial_header_font_size: Utils.dp(60) - readonly property int tutorial_content_header_h1_font_size: Utils.dp(26) - readonly property int tutorial_content_header_h2_font_size: Utils.dp(20) - readonly property int tutorial_content_font_size: Utils.dp(16) - - readonly property int menubar_width: Utils.dp(60) - - readonly property int titlebar_height: Utils.dp(48) - readonly property int titlebar_padding: Utils.dp(12) - readonly property int titlebar_spacing: Utils.dp(18) - - readonly property int component_spacing: Utils.dp(20) - readonly property int pane_padding: Utils.dp(20) - readonly property int pane_spacing: Utils.dp(20) - readonly property int groupbox_spacing: Utils.dp(10) + readonly property int component_spacing: 20 + readonly property int pane_padding: 20 + readonly property int pane_spacing: 20 + readonly property int groupbox_spacing: 10 readonly property bool is_desktop: false - - function thresholdReduce(value) { - var w = Screen.width - if (w > 415) { - return Utils.dp(value) - } - return Utils.dp(value * w / 415) - } } diff --git a/resources/qml/Governikus/Global/+mobile/SwipeActionDelegate.qml b/resources/qml/Governikus/Global/+mobile/SwipeActionDelegate.qml new file mode 100644 index 0000000..927e70d --- /dev/null +++ b/resources/qml/Governikus/Global/+mobile/SwipeActionDelegate.qml @@ -0,0 +1,110 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +Rectangle { + id: baseItem + + property alias contentItem: content.children + property alias pressed: mouseArea.downOnContent + property alias actionColor: baseItem.color + property alias actionIcon: actionImage.source + property string actionAccessibleName + + signal clicked + signal actionClicked + + height: content.childrenRect.height + width: parent.width + + Image { + Accessible.role: Accessible.Button + Accessible.name: actionAccessibleName + Accessible.onPressAction: baseItem.actionClicked() + + id: actionImage + anchors { + top: parent.top + bottom: parent.bottom + right: parent.right + margins: 20 + } + width: Math.abs(mouseArea.actionOpenOffset) - anchors.margins * 2 + fillMode: Image.PreserveAspectFit + } + + Item { + id: content + + width: parent.width + height: parent.height + + Behavior on x { + NumberAnimation { + duration: Constants.animation_duration + onRunningChanged: { + if (!running && content.x <= -content.width) { + baseItem.actionClicked() + } + } + } + } + } + + MouseArea { + id: mouseArea + + readonly property real actionOpenOffset: -parent.width / 4 + readonly property bool isActionOpen: contentX === actionOpenOffset + + readonly property bool downOnContent: pressed && startX < contentStartX + content.width + readonly property bool downOnAction: pressed && !downOnContent + readonly property bool deleteOffsetReached: contentX < 2 * actionOpenOffset + property bool didDrag: false + + property real startX: 0.0 + property alias contentX: content.x + property real contentStartX: 0.0 + + anchors.fill: parent + + drag { + target: content + axis: Drag.XAxis + maximumX: 0 + minimumX: -content.width + filterChildren: true + threshold: 10 + onActiveChanged: didDrag = true + } + + onPressed: { + startX = mouse.x + contentStartX = content.x + didDrag = false + } + + onReleased: { + if (didDrag) { + if (contentX > actionOpenOffset / 2) { + contentX = 0 + } else if (deleteOffsetReached) { + contentX = drag.minimumX + } else { + contentX = actionOpenOffset + } + } else if (downOnContent) { + if (isActionOpen) { + contentX = 0 + } else { + baseItem.clicked() + } + } else if (downOnAction) { + baseItem.actionClicked() + } + } + } +} diff --git a/resources/qml/Governikus/Global/BaseConfirmationPopup.qml b/resources/qml/Governikus/Global/BaseConfirmationPopup.qml new file mode 100644 index 0000000..9d80daa --- /dev/null +++ b/resources/qml/Governikus/Global/BaseConfirmationPopup.qml @@ -0,0 +1,124 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +Popup { + id: root + + enum PopupStyle { + NoButtons = 0, + OkButton = 1, + CancelButton = 2 + } + + property alias buttons: buttonContainer.children + default property alias children: customContent.children + property string title + property string text + //: LABEL ALL_PLATFORMS + property string okButtonText: qsTr("Ok") + SettingsModel.translationTrigger + //: LABEL ALL_PLATFORMS + property string cancelButtonText: qsTr("Cancel") + SettingsModel.translationTrigger + property int style: ConfirmationPopup.PopupStyle.OkButton | ConfirmationPopup.PopupStyle.CancelButton + property var horizontalTextAlignment: Text.AlignLeft + + signal confirmed + signal cancelled + + function accept(){ + root.confirmed() + if (root.closePolicy !== Popup.NoAutoClose) { + root.close() + } + } + + function cancel() { + root.cancelled() + if (root.closePolicy !== Popup.NoAutoClose) { + root.close() + } + } + + anchors.centerIn: Overlay.overlay + margins: Constants.pane_padding + padding: 0 + topPadding: Constants.pane_padding + + modal: true + closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape + + background: Rectangle { + color: Style.color.background_popup + radius: Style.dimens.corner_radius_popup + } + + ColumnLayout { + id: contentLayout + + width: root.availableWidth + spacing: Constants.pane_padding + + GText { + visible: text !== "" + Layout.fillWidth: true + Layout.rightMargin: Constants.pane_padding + Layout.leftMargin: Constants.pane_padding + + activeFocusOnTab: true + + text: root.title + textStyle: Style.text.header + font.bold: true + horizontalAlignment: root.horizontalTextAlignment + + FocusFrame { + dynamic: false + } + } + + GText { + visible: text !== "" + Layout.fillWidth: true + Layout.rightMargin: Constants.pane_padding + Layout.leftMargin: Constants.pane_padding + + activeFocusOnTab: true + + text: root.text + textStyle: Style.text.normal + horizontalAlignment: root.horizontalTextAlignment + + FocusFrame { + dynamic: false + } + } + + Column { + id: customContent + visible: children.length > 0 + + Layout.fillWidth: true + Layout.rightMargin: Constants.pane_padding + Layout.leftMargin: Constants.pane_padding + } + + Item { + id: buttonContainer + + Layout.fillWidth: true + height: childrenRect.height + } + } +} + diff --git a/resources/qml/Governikus/Global/Constants.qml b/resources/qml/Governikus/Global/Constants.qml index 2b89145..a1f7982 100644 --- a/resources/qml/Governikus/Global/Constants.qml +++ b/resources/qml/Governikus/Global/Constants.qml @@ -1,9 +1,11 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + pragma Singleton import QtQuick 2.10 -import Governikus.Type.ApplicationModel 1.0 - PlatformConstants { readonly property color green: "#a3cb7f" readonly property color red: "#cc0000" @@ -13,4 +15,6 @@ PlatformConstants { readonly property color black: "#000000" readonly property double scrolling_speed: 7500.0 + + readonly property int animation_duration: 250 } diff --git a/resources/qml/Governikus/Global/GSeparator.qml b/resources/qml/Governikus/Global/GSeparator.qml new file mode 100644 index 0000000..41306b8 --- /dev/null +++ b/resources/qml/Governikus/Global/GSeparator.qml @@ -0,0 +1,16 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Style 1.0 + +Rectangle { + property int orientation: Qt.Horizontal // Qt.Vertical + + height: orientation === Qt.Horizontal ? Style.dimens.separator_size : undefined + width: orientation === Qt.Vertical ? Style.dimens.separator_size : undefined + + color: Style.color.border +} diff --git a/resources/qml/Governikus/Global/GText.qml b/resources/qml/Governikus/Global/GText.qml new file mode 100644 index 0000000..ab63e0b --- /dev/null +++ b/resources/qml/Governikus/Global/GText.qml @@ -0,0 +1,31 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import Governikus.Style 1.0 + +Text { + property TextStyle textStyle: Style.text.normal + + Accessible.role: Accessible.StaticText + Accessible.name: text + + color: textStyle.textColor + font.pixelSize: textStyle.textSize + + wrapMode: Text.Wrap + + onTextStyleChanged: { + if (textStyle.textFamily !== "") { + font.family = textStyle.textFamily + } + } + + MouseArea { + anchors.fill: parent + + acceptedButtons: Qt.NoButton + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } +} diff --git a/resources/qml/Governikus/Global/PaneTitle.qml b/resources/qml/Governikus/Global/PaneTitle.qml deleted file mode 100644 index 4087927..0000000 --- a/resources/qml/Governikus/Global/PaneTitle.qml +++ /dev/null @@ -1,11 +0,0 @@ -import QtQuick 2.10 - - -Text { - anchors.left: parent.left - anchors.right: parent.right - visible: text !== "" - font.pixelSize: Constants.pane_title_font_size - color: Constants.blue - elide: Text.ElideRight -} diff --git a/resources/qml/Governikus/Global/StatusIcon.qml b/resources/qml/Governikus/Global/StatusIcon.qml index c8878b7..0254ec1 100644 --- a/resources/qml/Governikus/Global/StatusIcon.qml +++ b/resources/qml/Governikus/Global/StatusIcon.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 @@ -8,13 +12,12 @@ Rectangle { property alias busy: busyIndicator.visible property alias source: image.source property alias text: text.text - property alias font: text.font width: height radius: height / 2 border.width: height / 40; border.color: Constants.white - color: "transparent" + color: Style.color.transparent BusyIndicator { id: busyIndicator @@ -39,12 +42,10 @@ Rectangle { visible: source.toString().length > 0 } - Text { + GText { id: text anchors.centerIn: parent - color: Constants.blue - font.bold: true - font.pixelSize: Constants.header_font_size + textStyle: Style.text.title_accent visible: text !== "" } } diff --git a/resources/qml/Governikus/Global/Utils.js b/resources/qml/Governikus/Global/Utils.js index f161cac..40b5c7b 100644 --- a/resources/qml/Governikus/Global/Utils.js +++ b/resources/qml/Governikus/Global/Utils.js @@ -1,8 +1,3 @@ -function noTest() -{ - return typeof(plugin) !== 'undefined' -} - function escapeHtml(str) { return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); @@ -41,15 +36,30 @@ function getRandomInt(min, max) return Math.floor(Math.random() * (max - min)) + min; } -var contentScaleFactor = noTest() ? screenDpiScale : 1 - -function dp(value) -{ - return value * contentScaleFactor -} - function providerIconSource(baseName) { var platform = plugin.platformStyle.indexOf("tablet") !== -1 ? "+tablet/" : "" return "qrc:///images/provider/" + platform + baseName + ".png" } + +function scrollPageDown(pFlickable) { + if (pFlickable.height >= pFlickable.contentHeight) { + return + } + + pFlickable.contentY += pFlickable.height + if (pFlickable.contentY + pFlickable.height > pFlickable.contentHeight) { + pFlickable.contentY = pFlickable.contentHeight - pFlickable.height + } +} + +function scrollPageUp(pFlickable) { + if (pFlickable.height >= pFlickable.contentHeight) { + return + } + + pFlickable.contentY -= pFlickable.height + if (pFlickable.contentY < 0) { + pFlickable.contentY = 0 + } +} diff --git a/resources/qml/Governikus/Global/qmldir b/resources/qml/Governikus/Global/qmldir index 5f9b951..b3fb4e2 100644 --- a/resources/qml/Governikus/Global/qmldir +++ b/resources/qml/Governikus/Global/qmldir @@ -1,6 +1,7 @@ module Global singleton Constants 1.0 Constants.qml +internal BaseConfirmationPopup BaseConfirmationPopup.qml internal PlatformConstants PlatformConstants.qml internal BrandConstants BrandConstants.qml internal DeviceConstants DeviceConstants.qml @@ -9,16 +10,32 @@ Utils 1.0 Utils.js Category 1.0 Category.js ConfirmationPopup 1.0 ConfirmationPopup.qml -ContinueButton 1.0 ContinueButton.qml GButton 1.0 GButton.qml GCheckBox 1.0 GCheckBox.qml GComboBox 1.0 GComboBox.qml GRadioButton 1.0 GRadioButton.qml +GSeparator 1.0 GSeparator.qml GSwitch 1.0 GSwitch.qml GText 1.0 GText.qml GTextField 1.0 GTextField.qml +GFlickable 1.0 GFlickable.qml +GGridView 1.0 GGridView.qml +GListView 1.0 GListView.qml +GScrollBar 1.0 GScrollBar.qml LabeledText 1.0 LabeledText.qml +ListItem 1.0 ListItem.qml LocationButton 1.0 LocationButton.qml +NavigationButton 1.0 NavigationButton.qml Pane 1.0 Pane.qml +RoundedRectangle 1.0 RoundedRectangle.qml +ScrollablePane 1.0 ScrollablePane.qml +ScrollGradients 1.0 ScrollGradients.qml PaneTitle 1.0 PaneTitle.qml +SearchBar 1.0 SearchBar.qml StatusIcon 1.0 StatusIcon.qml +SwipeActionDelegate 1.0 SwipeActionDelegate.qml +TabbedPane 1.0 TabbedPane.qml +TabbedPaneDelegateIconAndText 1.0 TabbedPaneDelegateIconAndText.qml +TabbedPaneDelegateIconAndThreeLineText 1.0 TabbedPaneDelegateIconAndThreeLineText.qml +TabbedPaneDelegateOneLineText 1.0 TabbedPaneDelegateOneLineText.qml +ToggleableOption 1.0 ToggleableOption.qml diff --git a/resources/qml/Governikus/HistoryView/+android/+phone/HistoryView.qml b/resources/qml/Governikus/HistoryView/+android/+phone/HistoryView.qml deleted file mode 100644 index b3045a6..0000000 --- a/resources/qml/Governikus/HistoryView/+android/+phone/HistoryView.qml +++ /dev/null @@ -1,61 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.View 1.0 - -SectionPage { - id: rootPage - property alias listViewModel: listView.model - property var selectedIndices: [] - - headerTitleBarAction: TitleBarAction { text: qsTr("History") + settingsModel.translationTrigger; font.bold: true } - rightTitleBarAction: HistoryViewTitleBarControls { - id: historyControls - deleteHistoryConfirmationPopup: deleteHistoryConfirmationPopup - } - - HistoryViewConfirmationPopup { - id: deleteHistoryConfirmationPopup - baseItem: rootPage - } - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("Currently there are no history entries.") + settingsModel.translationTrigger - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: listView.count === 0 - } - - ListView { - id: listView - anchors.fill: parent - model: historyModel - onContentYChanged: { - if (contentY < 0) { - // prevent flicking over the top - contentY = 0 - } - } - - delegate: - HistoryListViewDelegate { - id: historyDelegate - anchors.left: parent.left - anchors.right: parent.right - height: Utils.dp(120) - listModel: historyModel - property var historyModelItem: model - showDetail: false - } - } - - HistoryViewDetails { - id: detailsHistoryView - visible: false - } -} diff --git a/resources/qml/Governikus/HistoryView/+android/+tablet/HistoryView.qml b/resources/qml/Governikus/HistoryView/+android/+tablet/HistoryView.qml deleted file mode 100644 index fe88448..0000000 --- a/resources/qml/Governikus/HistoryView/+android/+tablet/HistoryView.qml +++ /dev/null @@ -1,67 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.Provider 1.0 -import Governikus.View 1.0 - -SectionPage { - id: rootPage - property alias listViewModel: listView.model - property var selectedIndices: [] - - headerTitleBarAction: TitleBarAction { text: qsTr("History") + settingsModel.translationTrigger; font.bold: true } - rightTitleBarAction: HistoryViewTitleBarControls { - id: historyControls - deleteHistoryConfirmationPopup: deleteHistoryConfirmationPopup - } - - HistoryViewConfirmationPopup { - id: deleteHistoryConfirmationPopup - baseItem: rootPage - } - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("Currently there are no history entries.") + settingsModel.translationTrigger - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: listView.count === 0 - } - - ListView { - id: listView - anchors.fill: parent - model: historyModel - onContentYChanged: { - if (contentY < 0) { - // prevent flicking over the top - contentY = 0 - } - } - - delegate: - HistoryListViewDelegate { - id: historyDelegate - anchors.left: parent.left - anchors.right: parent.right - height: Utils.dp(120) - property var historyModelItem: model - listModel: historyModel - showDetail: false - } - } - - ProviderDetailView { - id: providerHistoryView - visible: false - } - - HistoryViewDetails { - id: detailsHistoryView - visible: false - } -} diff --git a/resources/qml/Governikus/HistoryView/+android/HistoryItemImage.qml b/resources/qml/Governikus/HistoryView/+android/HistoryItemImage.qml deleted file mode 100644 index e5ac63a..0000000 --- a/resources/qml/Governikus/HistoryView/+android/HistoryItemImage.qml +++ /dev/null @@ -1,51 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Item { - id: baseItem - - property string imageUrl: "" - - readonly property int imageMargin: Utils.dp(10) - - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - - height: parent.height - width: Utils.dp(80) - - Rectangle { - id: background - anchors.centerIn: parent - height: parent.height - 2 * parent.imageMargin - width: parent.width - 2 * parent.imageMargin - color: historyModelItem ? Category.displayColor(historyModelItem.providerCategory) : Category.displayColor("unknown" ) - - visible: baseItem.imageUrl !== "" - } - - Image { - id: categoryImage - anchors.centerIn: parent - height: parent.height - 2 * parent.imageMargin - width: parent.width - 2 * parent.imageMargin - source: historyModelItem ? Category.sectionImageSource(historyModelItem.providerCategory) : Category.sectionImageSource("unknown") - asynchronous: true - clip: true - - visible: baseItem.imageUrl === "" - } - - Image { - id: foregroundImage - source: baseItem.imageUrl - anchors.fill: background - anchors.margins: parent.imageMargin - asynchronous: true - - visible: baseItem.imageUrl !== "" - fillMode: Image.PreserveAspectFit - } -} diff --git a/resources/qml/Governikus/HistoryView/+android/HistoryListViewDelegate.qml b/resources/qml/Governikus/HistoryView/+android/HistoryListViewDelegate.qml deleted file mode 100644 index 257523c..0000000 --- a/resources/qml/Governikus/HistoryView/+android/HistoryListViewDelegate.qml +++ /dev/null @@ -1,23 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Rectangle { - property alias showDetail: contentItem.showDetail - property alias listModel: contentItem.listModel - - HistoryListViewDelegateContent { - id: contentItem - height: parent.height - borderLine.height - width: parent.width - anchors.top: parent.top - } - Rectangle { - id: borderLine - color: "black" - height: Utils.dp(1) - width: parent.width - anchors.top: contentItem.bottom - anchors.bottom: parent.bottom - } -} diff --git a/resources/qml/Governikus/HistoryView/+desktop/HistoryRemovalTimePeriodControl.qml b/resources/qml/Governikus/HistoryView/+desktop/HistoryRemovalTimePeriodControl.qml new file mode 100644 index 0000000..9484fec --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+desktop/HistoryRemovalTimePeriodControl.qml @@ -0,0 +1,64 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany +*/ + +import QtQuick 2.10 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +RowLayout { + id: root + + property string period + property alias description: description + property alias removalPeriod: removalPeriod + + Accessible.role: Accessible.Heading + Accessible.name: description.text + + spacing: Constants.groupbox_spacing + + GText { + id: description + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Time period") + SettingsModel.translationTrigger + + FocusFrame { + dynamic: false + } + } + + GComboBox { + id: removalPeriod + + Layout.fillWidth: true + height: ApplicationModel.scaleFactor * 50 + + activeFocusOnTab: true + model: ListModel { + id: timePeriods + + //: LABEL DESKTOP_QML + ListElement { modelData: qsTr("Past hour"); value: "PAST_HOUR" } + //: LABEL DESKTOP_QML + ListElement { modelData: qsTr("Past day"); value: "PAST_DAY" } + //: LABEL DESKTOP_QML + ListElement { modelData: qsTr("Past week"); value: "PAST_WEEK" } + //: LABEL DESKTOP_QML + ListElement { modelData: qsTr("Last four weeks"); value: "LAST_FOUR_WEEKS" } + //: LABEL DESKTOP_QML + ListElement { modelData: qsTr("All history"); value: "ALL_HISTORY" } + } + + onCurrentIndexChanged: root.period = timePeriods.get(currentIndex).value + } +} diff --git a/resources/qml/Governikus/HistoryView/+desktop/HistoryView.qml b/resources/qml/Governikus/HistoryView/+desktop/HistoryView.qml new file mode 100644 index 0000000..9039037 --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+desktop/HistoryView.qml @@ -0,0 +1,147 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany +*/ + +import QtQml 2.10 +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.HistoryModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.View 1.0 + +SectionPage { + id: root + + Accessible.name: qsTr("History view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the history view of the AusweisApp2.") + SettingsModel.translationTrigger + + ConfirmationPopup { + id: deleteHistoryConfirmationPopup + + //: INFO DESKTOP_QML Header of the confirmation dialog to clear the entire authentication history. + title: qsTr("Delete history?") + SettingsModel.translationTrigger + //: INFO DESKTOP_QML Content of the confirmation dialog to clear the entire authentication history. + text: qsTr("Please confirm that you want to delete your history entries.") + SettingsModel.translationTrigger + + onConfirmed: { + var timePeriod = removalPeriod.period + var removedItemCount = SettingsModel.removeHistory(timePeriod) + tabbedPane.currentIndex = tabbedPane.count > 0 ? 0 : -1 + //: INFO DESKTOP_QML Feedback how many history entries were removed. + ApplicationModel.showFeedback(qsTr("Removed %1 entries from the history.").arg(removedItemCount)) + } + + HistoryRemovalTimePeriodControl { + id: removalPeriod + width: parent.width + } + } + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("History") + SettingsModel.translationTrigger + customSubAction: SearchBar { + id: searchBar + + onDisplayTextChanged: HistoryModel.searchFilter.setFilterString(displayText) + } + } + + onVisibleChanged: if (!visible) deleteHistoryConfirmationPopup.close() + + TabbedPane { + id: tabbedPane + + visible: sectionCount > 0 + anchors.fill: parent + anchors.margins: Constants.pane_padding + + sectionsModel: HistoryModel.searchFilter + + sectionDelegate: TabbedPaneDelegateIconAndThreeLineText { + headerText: (model ? ( Utils.isToday(model.dateTime) ? qsTr("today") : + Utils.isYesterday(model.dateTime) ? qsTr("yesterday") : + Utils.isThisWeek(model.dateTime) ? model.dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : + model.dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) ) : "") + SettingsModel.translationTrigger + mainText: model ? model.subject : "" + footerText: model ? model.purpose : "" + iconPath: model ? model.providerIcon : "" + } + + contentDelegate: HistoryViewDetails { + historyModelItem: tabbedPane.currentItemModel ? tabbedPane.currentItemModel.model : undefined + + activeFocusOnTab: true + } + + footerItem: Item { + height: Math.max(clearHistoryButton.implicitHeight, saveHistoryToPdf.implicitHeight) + + GButton { + id: clearHistoryButton + + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: parent.horizontalCenter + rightMargin: (Constants.groupbox_spacing / 2) + } + + Accessible.name: text + activeFocusOnTab: true + + icon.source: "qrc:///images/trash_icon_white.svg" + //: LABEL DESKTOP_QML + text: qsTr("Clear history") + SettingsModel.translationTrigger + onClicked: deleteHistoryConfirmationPopup.open() + } + + GButton { + id: saveHistoryToPdf + + anchors { + top: parent.top + bottom: parent.bottom + left: parent.horizontalCenter + right: parent.right + rightMargin: Constants.groupbox_spacing + leftMargin: Math.floor(Constants.groupbox_spacing / 2) + } + + Accessible.name: text + activeFocusOnTab: true + + icon.source: "qrc:///images/icon_save.svg" + //: LABEL DESKTOP_QML + text: qsTr("Save to pdf") + SettingsModel.translationTrigger + onClicked: { + var now = new Date().toLocaleDateString(Qt.locale(), "yyyy-MM-dd") + var filenameSuggestion = "%1.%2.%3.pdf".arg(Qt.application.name).arg(qsTr("History")).arg(now) + console.log("filenameSuggestion", filenameSuggestion) + appWindow.openSaveFileDialog(HistoryModel.exportHistory, filenameSuggestion, "pdf") + } + } + } + } + + GText { + id: textNoHistoryEntries + + visible: tabbedPane.sectionCount === 0 + anchors.centerIn: parent + + Accessible.name: text + activeFocusOnTab: true + + //: INFO DESKTOP_QML No authentication history, placeholder text. + text: qsTr("Currently there are no history entries.") + SettingsModel.translationTrigger + textStyle: Style.text.header + } +} diff --git a/resources/qml/Governikus/HistoryView/+desktop/HistoryViewDetails.qml b/resources/qml/Governikus/HistoryView/+desktop/HistoryViewDetails.qml new file mode 100644 index 0000000..4fa72dc --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+desktop/HistoryViewDetails.qml @@ -0,0 +1,104 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + + +Item { + id: root + property var historyModelItem + + height: columnLayout.height + + Accessible.name: qsTr("Details for history entry") + SettingsModel.translationTrigger + + ColumnLayout { + id: columnLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Constants.pane_spacing + + GText { + text: qsTr("Provider Information") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + } + + LabeledText { + id: providerName + + Layout.fillWidth: true + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + label: qsTr("Provider name") + SettingsModel.translationTrigger + text: historyModelItem ? historyModelItem.subject : "" + } + + LabeledText { + id: purpose + + Layout.fillWidth: true + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + label: qsTr("Purpose") + SettingsModel.translationTrigger + text: historyModelItem ? historyModelItem.purpose : "" + } + + LabeledText { + id: date + + Layout.fillWidth: true + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + label: qsTr("Date") + SettingsModel.translationTrigger + text:{ + if (!historyModelItem) { + return ""; + } + return historyModelItem.dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) + SettingsModel.translationTrigger + } + textUppercase: Font.AllUppercase + } + + LabeledText { + id: requestedData + + Layout.fillWidth: true + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + label: qsTr("Requested data") + SettingsModel.translationTrigger + text: historyModelItem ? historyModelItem.requestedData : "" + } + + LabeledText { + id: termsOfUsage + + Layout.fillWidth: true + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + label: qsTr("Terms of usage") + SettingsModel.translationTrigger + text: historyModelItem ? historyModelItem.termsOfUsage : "" + } + } +} diff --git a/resources/qml/Governikus/HistoryView/+ios/+phone/HistoryView.qml b/resources/qml/Governikus/HistoryView/+ios/+phone/HistoryView.qml deleted file mode 100644 index c194efa..0000000 --- a/resources/qml/Governikus/HistoryView/+ios/+phone/HistoryView.qml +++ /dev/null @@ -1,101 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.View 1.0 - -SectionPage { - id: baseItem - - leftTitleBarAction: TitleBarAction { - id: leftAction - - state: "edit" - states: [ - State { name: "edit"; when: !historyListView.editMode }, - State { name: "cancel"; when: historyListView.editMode } - ] - onClicked: { - if (state === "edit") { - state = "cancel" - historyListView.editMode = true - } - else { - state = "edit" - historyListView.editMode = false - } - } - } - - headerTitleBarAction: Text { text: qsTr("History") + settingsModel.translationTrigger; font.bold: true } - - rightTitleBarAction: TitleBarAction { - id: rightAction - - states: [ - State { - name: "none" - when: leftAction.state === "edit" - PropertyChanges { target: rightAction; text: "" } - }, - State { - name: "deleteAll" - when: historyListView.editMode - PropertyChanges { target: rightAction; text: qsTr("Delete all") + settingsModel.translationTrigger } - } - ] - onClicked: { - if (historyListView.editMode){ - deleteHistoryConfirmationPopup.setValues("ALL_HISTORY", qsTr("Please confirm that you want to delete your complete history.")) - deleteHistoryConfirmationPopup.open() - } - historyListView.editMode = false - } - } - - HistoryViewConfirmationPopup { - id: deleteHistoryConfirmationPopup - baseItem: baseItem - } - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("Currently there are no history entries.") + settingsModel.translationTrigger - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: historyListView.count === 0 - } - - ListView { - id: historyListView - anchors.fill: parent - focus: true - spacing: Utils.dp(5) - - property bool editMode: false - - model: historyModel - delegate: HistoryListViewDelegate { - historyModelItem: model - - onClicked: { - detailsHistoryView.historyModelItem = historyModelItem - firePush(detailsHistoryView) - } - - Connections { - target: historyListView - onEditModeChanged: { - reactToEditModeChanged(historyListView.editMode) - } - } - } - } - - HistoryViewDetails { - id: detailsHistoryView - visible: false - } -} diff --git a/resources/qml/Governikus/HistoryView/+ios/+tablet/HistoryView.qml b/resources/qml/Governikus/HistoryView/+ios/+tablet/HistoryView.qml deleted file mode 100644 index c194efa..0000000 --- a/resources/qml/Governikus/HistoryView/+ios/+tablet/HistoryView.qml +++ /dev/null @@ -1,101 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.View 1.0 - -SectionPage { - id: baseItem - - leftTitleBarAction: TitleBarAction { - id: leftAction - - state: "edit" - states: [ - State { name: "edit"; when: !historyListView.editMode }, - State { name: "cancel"; when: historyListView.editMode } - ] - onClicked: { - if (state === "edit") { - state = "cancel" - historyListView.editMode = true - } - else { - state = "edit" - historyListView.editMode = false - } - } - } - - headerTitleBarAction: Text { text: qsTr("History") + settingsModel.translationTrigger; font.bold: true } - - rightTitleBarAction: TitleBarAction { - id: rightAction - - states: [ - State { - name: "none" - when: leftAction.state === "edit" - PropertyChanges { target: rightAction; text: "" } - }, - State { - name: "deleteAll" - when: historyListView.editMode - PropertyChanges { target: rightAction; text: qsTr("Delete all") + settingsModel.translationTrigger } - } - ] - onClicked: { - if (historyListView.editMode){ - deleteHistoryConfirmationPopup.setValues("ALL_HISTORY", qsTr("Please confirm that you want to delete your complete history.")) - deleteHistoryConfirmationPopup.open() - } - historyListView.editMode = false - } - } - - HistoryViewConfirmationPopup { - id: deleteHistoryConfirmationPopup - baseItem: baseItem - } - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("Currently there are no history entries.") + settingsModel.translationTrigger - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - visible: historyListView.count === 0 - } - - ListView { - id: historyListView - anchors.fill: parent - focus: true - spacing: Utils.dp(5) - - property bool editMode: false - - model: historyModel - delegate: HistoryListViewDelegate { - historyModelItem: model - - onClicked: { - detailsHistoryView.historyModelItem = historyModelItem - firePush(detailsHistoryView) - } - - Connections { - target: historyListView - onEditModeChanged: { - reactToEditModeChanged(historyListView.editMode) - } - } - } - } - - HistoryViewDetails { - id: detailsHistoryView - visible: false - } -} diff --git a/resources/qml/Governikus/HistoryView/+ios/HistoryDetails.qml b/resources/qml/Governikus/HistoryView/+ios/HistoryDetails.qml deleted file mode 100644 index aa43a18..0000000 --- a/resources/qml/Governikus/HistoryView/+ios/HistoryDetails.qml +++ /dev/null @@ -1,33 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Item { - property string providerAddress: "" - - property int listItemIndex: -1 - - property var listModel - - anchors.right: parent.right - anchors.margins: Utils.dp(5) - height: parent.height - width: parent.height * 0.4 - - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - height: width - - border.color: Constants.history_delegate_address_color - border.width: Utils.dp(1) - radius: width - - Text { - anchors.centerIn: parent - text: "i" - color: parent.border.color - } - } -} diff --git a/resources/qml/Governikus/HistoryView/+ios/HistoryItemImage.qml b/resources/qml/Governikus/HistoryView/+ios/HistoryItemImage.qml deleted file mode 100644 index ed59fe0..0000000 --- a/resources/qml/Governikus/HistoryView/+ios/HistoryItemImage.qml +++ /dev/null @@ -1,26 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Item { - property string imageUrl: "" - - id: baseItem - - anchors.left: parent.left - anchors.leftMargin: Utils.dp(10) - - height: parent.height - width: Utils.dp(40) - - Image { - source: baseItem.imageUrl !== "" ? - baseItem.imageUrl : - (historyModelItem ? Category.imageSource(historyModelItem.providerCategory) : Category.imageSource("unknown")) - asynchronous: true - height: Math.min(parent.height * 0.6, parent.width) - width: height - fillMode: Image.PreserveAspectFit - anchors.centerIn: parent - } -} diff --git a/resources/qml/Governikus/HistoryView/+ios/HistoryListViewDelegate.qml b/resources/qml/Governikus/HistoryView/+ios/HistoryListViewDelegate.qml deleted file mode 100644 index e02f246..0000000 --- a/resources/qml/Governikus/HistoryView/+ios/HistoryListViewDelegate.qml +++ /dev/null @@ -1,133 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 - -Item { - id: baseItem - property var historyModelItem: historyModel - - height: swipeComponent.height - width: parent.width - - signal clicked() - - function reactToEditModeChanged(pNewValue){ - if (pNewValue){ - swipeComponent.swipe.open(SwipeDelegate.Right) - } else { - swipeComponent.swipe.close() - } - } - - SwipeDelegate { - id: swipeComponent - height: Constants.history_section_height - anchors.left: parent.left - anchors.right: parent.right - - onClicked: { - baseItem.clicked() - } - - contentItem: Rectangle { - id: background - color: "white" - anchors.top: parent.top - anchors.bottom: parent.bottom - - HistoryItemImage { - id: categoryImage - imageUrl: providerIcon - visible: true - } - - Rectangle { - id: purposeObject - anchors.verticalCenter: parent.verticalCenter - anchors.left: categoryImage.right - anchors.leftMargin: Utils.dp(15) - anchors.right: parent.right - - Column { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - spacing: Constants.history_delegate_spacing - - Text { - id: dateTimeText - - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.label_font_size - font.capitalization: Font.AllUppercase - color: Constants.blue - text: (Utils.isToday(dateTime) ? qsTr("today") : - Utils.isYesterday(dateTime) ? qsTr("yesterday") : - Utils.isThisWeek(dateTime) ? dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : - dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) - ) + settingsModel.translationTrigger - } - Text { - id: subjectText - - anchors.left: parent.left - anchors.right: parent.right - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.label_font_size - wrapMode: Text.WordWrap - text: subject - } - Text { - id: purposeText - - anchors.left: parent.left - anchors.right: parent.right - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.small_font_size - color: Constants.history_delegate_address_color - wrapMode: Text.WordWrap - text: !!purpose ? historyModelItem.purpose : qsTr("Tap for more details") + settingsModel.translationTrigger - } - } - } - - Image { - height: Utils.dp(35) - source: "qrc:///images/arrowRight.svg" - anchors.right: parent.right - anchors.verticalCenter: purposeObject.verticalCenter - anchors.rightMargin: Utils.dp(2) - fillMode: Image.PreserveAspectFit - } - } - - swipe.right: Rectangle { - id: confirmDeletionRect - height: parent.height - width: parent.width / 4 - anchors.right: parent.right - anchors.top: parent.top - - color: Constants.red - border.width: Constants.groupbox_spacing - border.color: Constants.white - - MouseArea { - anchors.fill: parent - onClicked: { - historyModel.removeRows(index, 1) - } - } - - Image { - anchors.fill: parent - anchors.margins: Utils.dp(20) - source: "qrc:///images/trash_icon_white.svg" - fillMode: Image.PreserveAspectFit - } - } - } -} - diff --git a/resources/qml/Governikus/HistoryView/+mobile/+android/HistoryListViewDelegate.qml b/resources/qml/Governikus/HistoryView/+mobile/+android/HistoryListViewDelegate.qml new file mode 100644 index 0000000..5fb8d1b --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+mobile/+android/HistoryListViewDelegate.qml @@ -0,0 +1,36 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.HistoryModel 1.0 + +HistoryListItem { + id: listItem + + width: parent.width + + contentMarginRight: deleteButton.width + 2 * Constants.groupbox_spacing + + showRightArrow: false + + Image { + id: deleteButton + sourceSize.width: Style.dimens.small_icon_size + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Constants.groupbox_spacing + + source: "qrc:///images/trash_icon.svg" + fillMode: Image.PreserveAspectFit + + MouseArea { + anchors.fill: parent + + onClicked: HistoryModel.removeRows(historyModelItem ? historyModelItem.index : -1, 1) + } + } +} diff --git a/resources/qml/Governikus/HistoryView/+mobile/+android/HistoryView.qml b/resources/qml/Governikus/HistoryView/+mobile/+android/HistoryView.qml new file mode 100644 index 0000000..34a688a --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+mobile/+android/HistoryView.qml @@ -0,0 +1,19 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.View 1.0 + + +BaseHistoryView { + id: baseItem + + rightTitleBarAction: HistoryViewTitleBarControls { + showDeleteAll: baseItem.historyItemCount !== 0 + } +} diff --git a/resources/qml/Governikus/HistoryView/+mobile/+android/HistoryViewTitleBarControls.qml b/resources/qml/Governikus/HistoryView/+mobile/+android/HistoryViewTitleBarControls.qml new file mode 100644 index 0000000..e48b721 --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+mobile/+android/HistoryViewTitleBarControls.qml @@ -0,0 +1,59 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +RowLayout { + id: historyControls + + property bool showDeleteAll: true + + height: Style.dimens.titlebar_height + + spacing: Constants.component_spacing + + HistoryViewConfirmationPopup { + id: deleteHistoryConfirmationPopup + } + + GSwitch { + id: enableHistorySwitch + + color: Constants.green + initialState: SettingsModel.historyEnabled + onSwitched: { + SettingsModel.historyEnabled = enableHistorySwitch.isOn + //: LABEL ANDROID IOS + ApplicationModel.showFeedback((SettingsModel.historyEnabled ? qsTr("History enabled") : qsTr("History disabled")) + SettingsModel.translationTrigger) + } + } + + Image { + id: deleteEntriesButtonImage + + visible: historyControls.showDeleteAll + + sourceSize.height: 36 + sourceSize.width: 36 + + fillMode: Image.PreserveAspectFit + source: "qrc:///images/trash_icon_white.svg" + + MouseArea { + id: deleteEntriesButton + + anchors.fill: parent + + onClicked: deleteHistoryConfirmationPopup.open() + } + } + } diff --git a/resources/qml/Governikus/HistoryView/+mobile/+ios/HistoryListViewDelegate.qml b/resources/qml/Governikus/HistoryView/+mobile/+ios/HistoryListViewDelegate.qml new file mode 100644 index 0000000..55b8387 --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+mobile/+ios/HistoryListViewDelegate.qml @@ -0,0 +1,30 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.HistoryModel 1.0 +import Governikus.Type.SettingsModel 1.0 + +SwipeActionDelegate { + id: swipeComponent + + property var historyModelItem + + actionColor: Constants.red + actionIcon: "qrc:///images/trash_icon_white.svg" + //: INFO IOS Accessible name for the trash icon of a history entry. + actionAccessibleName: qsTr("Delete history entry: %1").arg(historyModelItem.subject) + SettingsModel.translationTrigger + + contentItem: HistoryListItem { + historyModelItem: swipeComponent.historyModelItem + mouseAreaEnabled: false + pressed: swipeComponent.pressed + } + + onActionClicked: HistoryModel.removeRows(index, 1) +} diff --git a/resources/qml/Governikus/HistoryView/+mobile/+ios/HistoryView.qml b/resources/qml/Governikus/HistoryView/+mobile/+ios/HistoryView.qml new file mode 100644 index 0000000..64a78b1 --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+mobile/+ios/HistoryView.qml @@ -0,0 +1,45 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.HistoryModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +BaseHistoryView { + id: baseItem + + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + rightTitleBarAction: TitleBarAction { + id: rightAction + + states: [ + State { + name: "none" + when: baseItem.historyItemCount === 0 + PropertyChanges { target: rightAction; text: "" } + }, + State { + name: "deleteAll" + when: baseItem.historyItemCount !== 0 + PropertyChanges { + target: rightAction + //: LABEL IOS + text: qsTr("Delete all") + SettingsModel.translationTrigger + } + } + ] + + onClicked: deleteHistoryConfirmationPopup.open() + } + + HistoryViewConfirmationPopup { + id: deleteHistoryConfirmationPopup + } +} diff --git a/resources/qml/Governikus/HistoryView/+mobile/BaseHistoryView.qml b/resources/qml/Governikus/HistoryView/+mobile/BaseHistoryView.qml new file mode 100644 index 0000000..9a8ec4b --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+mobile/BaseHistoryView.qml @@ -0,0 +1,68 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.HistoryModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +SectionPage { + id: rootPage + property alias listViewModel: listView.model + property alias historyItemCount: listView.count + + sectionPageFlickable: listView + + //: INFO ANDROID IOS + title: qsTr("History") + SettingsModel.translationTrigger + + GText { + anchors.centerIn: parent + //: INFO ANDROID IOS No authentication history, placeholder text. + text: qsTr("Currently there are no history entries.") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary + visible: listView.count === 0 + } + + GListView { + id: listView + + anchors.fill: parent + + model: HistoryModel + + remove: Transition { + NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: Constants.animation_duration / 2 } + } + + removeDisplaced: Transition { + SequentialAnimation { + PauseAnimation { duration: Constants.animation_duration / 2 } + NumberAnimation { properties: "y"; duration: Constants.animation_duration / 2 } + } + } + + delegate: HistoryListViewDelegate { + historyModelItem: model + + Accessible.onScrollDownAction: historyListView.positionViewAtIndex(index, ListView.Beginning) + Accessible.onScrollUpAction: historyListView.positionViewAtIndex(index, ListView.End) + + onClicked: firePushWithProperties(detailsHistoryView, {historyModelItem: historyModelItem}) + } + } + + Component { + id: detailsHistoryView + + HistoryViewDetails {} + } +} diff --git a/resources/qml/Governikus/HistoryView/+mobile/HistoryListItem.qml b/resources/qml/Governikus/HistoryView/+mobile/HistoryListItem.qml new file mode 100644 index 0000000..02b5946 --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+mobile/HistoryListItem.qml @@ -0,0 +1,29 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Type.HistoryModel 1.0 +import Governikus.Type.SettingsModel 1.0 + +ListItem { + property var historyModelItem + + height: 72 + + Accessible.description: qsTr("Click to view details of history entry.") + SettingsModel.translationTrigger + + icon: providerIcon !== "" ? providerIcon : (historyModelItem ? Category.imageSource(historyModelItem.providerCategory) : Category.imageSource("unknown")) + //: LABEL ANDROID IOS + headerText: (Utils.isToday(dateTime) ? qsTr("today") : + Utils.isYesterday(dateTime) ? qsTr("yesterday") : + Utils.isThisWeek(dateTime) ? dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : + dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) + ) + SettingsModel.translationTrigger + text: subject + //: LABEL ANDROID IOS + footerText: historyModelItem.purpose !== "" ? historyModelItem.purpose : qsTr("Tap for more details") + SettingsModel.translationTrigger +} diff --git a/resources/qml/Governikus/HistoryView/+mobile/HistoryViewConfirmationPopup.qml b/resources/qml/Governikus/HistoryView/+mobile/HistoryViewConfirmationPopup.qml new file mode 100644 index 0000000..05f0d18 --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+mobile/HistoryViewConfirmationPopup.qml @@ -0,0 +1,22 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + +ConfirmationPopup { + //: LABEL ANDROID IOS + title: qsTr("Delete history") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS Confirmaton popup to clear all history entries. + text: qsTr("Please confirm that you want to delete your complete history.") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + okButtonText: qsTr("Delete") + SettingsModel.translationTrigger + + onConfirmed: SettingsModel.removeHistory("ALL_HISTORY") +} diff --git a/resources/qml/Governikus/HistoryView/+mobile/HistoryViewDetails.qml b/resources/qml/Governikus/HistoryView/+mobile/HistoryViewDetails.qml new file mode 100644 index 0000000..5e39751 --- /dev/null +++ b/resources/qml/Governikus/HistoryView/+mobile/HistoryViewDetails.qml @@ -0,0 +1,93 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + + +SectionPage { + id: root + property var historyModelItem + + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + title: historyModelItem ? historyModelItem.subject : "" + titleBarColor: Category.displayColor(historyModelItem ? historyModelItem.providerCategory : "") + + content: Item { + height: pane.height + 2 * Constants.component_spacing + width: root.width + + Column { + anchors.fill: parent + anchors.margins: Constants.component_spacing + + Pane { + id: pane + //: LABEL ANDROID IOS + title: qsTr("Provider Information") + SettingsModel.translationTrigger + + LabeledText { + Accessible.onScrollDownAction: scrollPageDown() + Accessible.onScrollUpAction: scrollPageUp() + + //: LABEL ANDROID IOS + label: qsTr("Provider name") + SettingsModel.translationTrigger + text: historyModelItem ? historyModelItem.subject : "" + width: parent.width + } + + LabeledText { + Accessible.onScrollDownAction: scrollPageDown() + Accessible.onScrollUpAction: scrollPageUp() + + //: LABEL ANDROID IOS + label: qsTr("Purpose") + SettingsModel.translationTrigger + text: historyModelItem ? historyModelItem.purpose : "" + width: parent.width + } + + LabeledText { + Accessible.onScrollDownAction: scrollPageDown() + Accessible.onScrollUpAction: scrollPageUp() + + //: LABEL ANDROID IOS + label: qsTr("Date") + SettingsModel.translationTrigger + text:{ + if (!historyModelItem) { + return ""; + } + return historyModelItem.dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) + SettingsModel.translationTrigger + } + width: parent.width + } + + LabeledText { + Accessible.onScrollDownAction: scrollPageDown() + Accessible.onScrollUpAction: scrollPageUp() + + //: LABEL ANDROID IOS + label: qsTr("Requested data") + SettingsModel.translationTrigger + text: historyModelItem ? historyModelItem.requestedData : "" + width: parent.width + } + + LabeledText { + Accessible.onScrollDownAction: scrollPageDown() + Accessible.onScrollUpAction: scrollPageUp() + + //: LABEL ANDROID IOS + label: qsTr("Terms of usage") + SettingsModel.translationTrigger + text: historyModelItem ? historyModelItem.termsOfUsage : "" + width: parent.width + } + } + } + } +} diff --git a/resources/qml/Governikus/HistoryView/HistoryListViewDelegateContent.qml b/resources/qml/Governikus/HistoryView/HistoryListViewDelegateContent.qml deleted file mode 100644 index 214354e..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryListViewDelegateContent.qml +++ /dev/null @@ -1,135 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Item { - id: baseItem - - property bool showDetail: false - property var listModel - - Rectangle { - id: background - color: "white" - anchors.left: parent.left - anchors.leftMargin: Utils.dp(5) - anchors.right: parent.right - anchors.rightMargin: Utils.dp(5) - anchors.top: parent.top - anchors.bottom: parent.bottom - - HistoryItemImage { - id: categoryImage - imageUrl: historyModelItem ? historyModelItem.providerIcon : "" - visible: !showDetail - } - - Rectangle { - id: purposeObject - height: purposeColumn.height - property alias truncated: purposeText.truncated - - anchors.verticalCenter: parent.verticalCenter - anchors.left: showDetail ? parent.left : categoryImage.right - anchors.leftMargin: showDetail ? 0 : Utils.dp(15) - anchors.right: deleteButton.left - - Column { - id: purposeColumn - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - spacing: Constants.history_delegate_spacing - - Text { - id: dateTimeText - - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.label_font_size - font.capitalization: Font.AllUppercase - color: Constants.blue - text: (!historyModelItem ? "" : - Utils.isToday(historyModelItem.dateTime) ? qsTr("today") : - Utils.isYesterday(historyModelItem.dateTime) ? qsTr("yesterday") : - Utils.isThisWeek(historyModelItem.dateTime) ? historyModelItem.dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : - historyModelItem.dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) - ) + settingsModel.translationTrigger - } - Text { - id: subjectText - color: Constants.secondary_text - - anchors.left: parent.left - anchors.right: parent.right - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.label_font_size - wrapMode: Text.WordWrap - text: historyModelItem ? historyModelItem.subject : "" - } - Text { - id: purposeText - - anchors.left: parent.left - anchors.right: parent.right - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.small_font_size - color: Constants.history_delegate_address_color - wrapMode: Text.WordWrap - maximumLineCount: 2 - text: historyModelItem ? !!historyModelItem.purpose ? historyModelItem.purpose : qsTr("Tap for more details") + settingsModel.translationTrigger : "" - } - } - } - - Text { - visible: purposeText.truncated - anchors.left: purposeObject.right - anchors.bottom: purposeObject.bottom - font.pixelSize: Constants.small_font_size - color: Constants.history_delegate_address_color - text: "..." - } - - Image { - height: Utils.dp(35) - source: "qrc:///images/arrowRight.svg" - anchors.right: parent.right - anchors.verticalCenter: purposeObject.verticalCenter - anchors.rightMargin: Utils.dp(2) - fillMode: Image.PreserveAspectFit - } - - MouseArea { - anchors.fill: parent - onClicked: { - detailsHistoryView.historyModelItem = historyModelItem - firePush(detailsHistoryView) - } - } - - MouseArea { - id: deleteButton - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: Utils.dp(5) - height: parent.height * 0.3 - width: height - visible: Constants.is_layout_ios ? false : true - - onClicked: { - if (typeof(listModel) === "object") { - listModel.removeRows(historyModelItem ? historyModelItem.index : -1, 1) - } - } - - Image { - height: Utils.dp(21) - anchors.right: parent.right - anchors.top: parent.top - source: "qrc:///images/trash_icon.svg" - fillMode: Image.PreserveAspectFit - } - } - } -} diff --git a/resources/qml/Governikus/HistoryView/HistoryViewConfirmationPopup.qml b/resources/qml/Governikus/HistoryView/HistoryViewConfirmationPopup.qml deleted file mode 100644 index c779d11..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryViewConfirmationPopup.qml +++ /dev/null @@ -1,92 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 - -Popup { - id: popupObject - property string timePeriod: "UNKNOWN" - property string popupDescriptionString: "" - property var baseItem: Item { } - x: (baseItem.width - width) / 2 - y: (baseItem.height - height) / 2 - - function setValues(pPeriodToRemove, pNewDescriptionString) { - timePeriod = pPeriodToRemove - popupDescriptionString = pNewDescriptionString - } - - modal: true - focus: true - closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape - width: Utils.dp(250) - height: popupColumn.height - - Column { - id: popupColumn - width: parent.width - spacing: Constants.pane_spacing - padding: Constants.pane_padding - - Text { - id: header - color: Constants.secondary_text - text: qsTr("Delete history") + settingsModel.translationTrigger - font.pixelSize: Constants.header_font_size - font.bold: true - } - - Text { - id: info - color: Constants.secondary_text - width: parent.width - 2 * Constants.pane_padding - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - text: popupObject.popupDescriptionString - } - - Row { - id: buttonBox - anchors.right: parent.right - anchors.rightMargin: Constants.pane_padding - spacing: Constants.pane_spacing - - MouseArea { - height: textItemCancel.height + 2 * Utils.dp(10) - width: textItemCancel.width + 2 * Utils.dp(10) - - Text { - id: textItemCancel - anchors.centerIn: parent - text: qsTr("Cancel") + settingsModel.translationTrigger - color: Constants.blue - font.pixelSize: Constants.titlebar_font_size - } - - onClicked: { - popupObject.close() - } - } - - MouseArea { - height: textItemDelete.height + 2 * Utils.dp(10) - width: textItemDelete.width + 2 * Utils.dp(10) - - Text { - id: textItemDelete - anchors.centerIn: parent - text: qsTr("Delete") + settingsModel.translationTrigger - color: Constants.blue - font.pixelSize: Constants.titlebar_font_size - } - - onClicked: { - var removedItems = settingsModel.removeHistory(popupObject.timePeriod); - qmlExtension.showFeedback(qsTr("Removed %1 entries from the history.").arg(removedItems)) - popupObject.close() - } - } - } - } -} - diff --git a/resources/qml/Governikus/HistoryView/HistoryViewDetails.qml b/resources/qml/Governikus/HistoryView/HistoryViewDetails.qml deleted file mode 100644 index 08a75ae..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryViewDetails.qml +++ /dev/null @@ -1,67 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.View 1.0 - -SectionPage { - id: root - property var historyModelItem - - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: historyModelItem ? historyModelItem.subject : ""; font.bold: true } - titleBarColor: Category.displayColor(historyModelItem ? historyModelItem.providerCategory : "") - - content: Item { - height: pane.height + 2 * Constants.component_spacing - width: root.width - - Column { - anchors.fill: parent - anchors.margins: Constants.component_spacing - - Pane { - id: pane - title: qsTr("Provider Information") + settingsModel.translationTrigger - - LabeledText { - label: qsTr("Provider name") + settingsModel.translationTrigger - text: historyModelItem ? historyModelItem.subject : "" - width: parent.width - } - - LabeledText { - label: qsTr("Purpose") + settingsModel.translationTrigger - text: historyModelItem ? historyModelItem.purpose : "" - width: parent.width - } - - LabeledText { - label: qsTr("Date") + settingsModel.translationTrigger - text:{ - if (!historyModelItem) { - return ""; - } - return historyModelItem.dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) + settingsModel.translationTrigger - } - width: parent.width - fontUppercase: Font.AllUppercase - } - - LabeledText { - label: qsTr("Requested data") + settingsModel.translationTrigger - text: historyModelItem ? historyModelItem.requestedData : "" - width: parent.width - } - - LabeledText { - label: qsTr("Terms of usage") + settingsModel.translationTrigger - text: historyModelItem ? historyModelItem.termsOfUsage : "" - width: parent.width - } - } - } - } -} diff --git a/resources/qml/Governikus/HistoryView/HistoryViewTitleBarControls.qml b/resources/qml/Governikus/HistoryView/HistoryViewTitleBarControls.qml deleted file mode 100644 index 92813e1..0000000 --- a/resources/qml/Governikus/HistoryView/HistoryViewTitleBarControls.qml +++ /dev/null @@ -1,45 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 - -Item { - id: historyControls - property var deleteHistoryConfirmationPopup: HistoryViewConfirmationPopup { } - anchors.verticalCenter: parent ? parent.verticalCenter : undefined - - GSwitch { - id: enableHistorySwitch - anchors.verticalCenter: parent.verticalCenter - anchors.rightMargin: Utils.dp(20) - anchors.right: deleteEntriesButton.left - - color: Constants.green - initialState: settingsModel.historyEnabled - onSwitched: { - settingsModel.historyEnabled = enableHistorySwitch.isOn - qmlExtension.showFeedback((settingsModel.historyEnabled ? qsTr("History enabled") : qsTr("History disabled")) + settingsModel.translationTrigger) - } - } - - MouseArea { - id: deleteEntriesButton - width: deleteEntriesButtonImage.width - height: deleteEntriesButtonImage.height - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: Utils.dp(10) - - onClicked: { - deleteHistoryConfirmationPopup.setValues("ALL_HISTORY", qsTr("Please confirm that you want to delete your complete history.")) - deleteHistoryConfirmationPopup.open() - } - - Image { - id: deleteEntriesButtonImage - height: Constants.titlebar_font_size * 1.5 - fillMode: Image.PreserveAspectFit - source: "qrc:///images/trash_icon_white.svg" - } - } - } diff --git a/resources/qml/Governikus/HistoryView/qmldir b/resources/qml/Governikus/HistoryView/qmldir index f90cead..04cb879 100644 --- a/resources/qml/Governikus/HistoryView/qmldir +++ b/resources/qml/Governikus/HistoryView/qmldir @@ -1,10 +1,11 @@ module HistoryView -internal HistoryDetails HistoryDetails.qml -internal HistoryItemImage HistoryItemImage.qml -internal HistoryListViewDelegateContent HistoryListViewDelegateContent.qml +internal BaseHistoryView BaseHistoryView.qml +internal HistoryViewConfirmationPopup HistoryViewConfirmationPopup.qml internal HistoryListViewDelegate HistoryListViewDelegate.qml +internal HistoryRemovalTimePeriodControl HistoryRemovalTimePeriodControl.qml internal HistoryViewDetails HistoryViewDetails.qml +internal HistoryListItem HistoryListItem.qml internal HistoryViewTitleBarControls HistoryViewTitleBarControls.qml HistoryView 1.0 HistoryView.qml diff --git a/resources/qml/Governikus/IdentifyView/+desktop/CertificateDescriptionPage.qml b/resources/qml/Governikus/IdentifyView/+desktop/CertificateDescriptionPage.qml index b3b47dd..9115352 100644 --- a/resources/qml/Governikus/IdentifyView/+desktop/CertificateDescriptionPage.qml +++ b/resources/qml/Governikus/IdentifyView/+desktop/CertificateDescriptionPage.qml @@ -1,45 +1,70 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { signal exit() + Accessible.name: qsTr("Self-authentication data view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the self-authentication data view of the AusweisApp2.") + SettingsModel.translationTrigger + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML text: qsTr("Provider Information") - showSettings: false + rootEnabled: false showHelp: false } Pane { id: pane + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right anchors.margins: Constants.pane_padding - title: qsTr("Provider Information") + settingsModel.translationTrigger + + //: LABEL DESKTOP_QML + title: qsTr("Provider Information") + SettingsModel.translationTrigger Repeater { id: listView + model: certificateDescriptionModel LabeledText { - id: delegate + width: parent.width + + activeFocusOnTab: true + label: model.label text: model.text textFormat: Text.PlainText - width: parent.width } } } GButton { id: button + anchors.right: pane.right anchors.bottom: pane.bottom anchors.margins: Constants.pane_padding - text: qsTr("Close") + + activeFocusOnTab: true + Accessible.name: button.text + + //: LABEL DESKTOP_QML + text: qsTr("Close") + SettingsModel.translationTrigger onClicked: parent.exit() } } diff --git a/resources/qml/Governikus/IdentifyView/+desktop/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+desktop/DataGroup.qml index 9e0eda3..10d66f4 100644 --- a/resources/qml/Governikus/IdentifyView/+desktop/DataGroup.qml +++ b/resources/qml/Governikus/IdentifyView/+desktop/DataGroup.qml @@ -1,99 +1,148 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 Column { - id: column - visible: count > 0 - spacing: Constants.pane_spacing + id: root + readonly property alias count: repeater.count property alias title: dataTitle.text property alias columns: grid.columns property alias chat: repeater.model - readonly property alias count: repeater.count + + visible: count > 0 + spacing: Constants.pane_spacing PaneTitle { id: dataTitle + + activeFocusOnTab: true + Accessible.role: Accessible.Heading + Accessible.name: dataTitle.text + + FocusFrame { + dynamic: false + border.color: Constants.black + } } Item { + visible: count < 1 width: parent.width height: noDataText * 1.5 - visible: count < 1 - Text { + GText { id: noDataText - color: Constants.black - anchors.verticalCenter: parent.verticalCenter + width: parent.width - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - text: qsTr("No data requested") + settingsModel.translationTrigger + anchors.verticalCenter: parent.verticalCenter + + activeFocusOnTab: true + Accessible.role: Accessible.Paragraph + Accessible.name: noDataText.text + + //: LABEL DESKTOP_QML + text: qsTr("No data requested") + SettingsModel.translationTrigger + textStyle: Style.text.normal_inverse } Rectangle { - anchors.bottom: parent.bottom height: Math.max(ApplicationModel.scaleFactor * 1, 1) width: parent.width + anchors.bottom: parent.bottom + color: Constants.grey } } Grid { id: grid - columnSpacing: Constants.pane_spacing + width: parent.width + + columnSpacing: Constants.pane_spacing verticalItemAlignment: Grid.AlignBottom Repeater { id: repeater + visible: count > 0 Item { + id: rightItem + + property alias checked: checkBox.checked + width: (grid.width - ((grid.columns - 1) * grid.columnSpacing)) / grid.columns height: dataText.height * 1.5 - Text { + activeFocusOnTab: true + Accessible.role: optional ? Accessible.CheckBox : Accessible.Paragraph + Accessible.name: dataText.text + (optional ? ": " + (selected ? qsTr("selected") : qsTr("not selected")) : "") + SettingsModel.translationTrigger + + Keys.onSpacePressed: if (optional) selected = !selected + + GText { id: dataText - color: Constants.black + anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.right: parent.right anchors.rightMargin: checkBox.visible ? checkBox.width + Constants.pane_spacing : 0 - font.pixelSize: Constants.normal_font_size + text: name - wrapMode: Text.WordWrap + textStyle: Style.text.normal_inverse + + FocusFrame { + scope: rightItem + marginFactor: 0.7 + border.color: Constants.black + dynamic: false + } } Rectangle { - anchors.bottom: parent.bottom height: Math.max(ApplicationModel.scaleFactor * 1, 1) width: parent.width + anchors.bottom: parent.bottom + color: Constants.grey } GCheckBox { id: checkBox + + visible: optional anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - height: parent.height / 2 - width: height - visible: optional + + activeFocusOnTab: false + checked: selected } MouseArea { anchors.fill: parent + enabled: optional onClicked: selected = !selected Rectangle { anchors.fill: parent - color: Constants.accent_color + + color: Style.color.accent opacity: parent.pressed ? 0.5 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } } } diff --git a/resources/qml/Governikus/IdentifyView/+desktop/EditRights.qml b/resources/qml/Governikus/IdentifyView/+desktop/EditRights.qml index f0f6a92..0e3fc31 100644 --- a/resources/qml/Governikus/IdentifyView/+desktop/EditRights.qml +++ b/resources/qml/Governikus/IdentifyView/+desktop/EditRights.qml @@ -1,12 +1,19 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 -import Governikus.Type.AuthModel 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.AuthModel 1.0 +import Governikus.Type.NumberModel 1.0 SectionPage { @@ -16,29 +23,59 @@ SectionPage { function showProviderInformation(show) { detailView = show + if (!detailView) onVisibleChanged() ApplicationWindow.menuBar.updateActions() } + Accessible.name: qsTr("Edit rights view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the edit rights view of the AusweisApp2.") + SettingsModel.translationTrigger + Keys.onReturnPressed: d.onKeyboardConfirmPressed(event) + Keys.onEnterPressed: d.onKeyboardConfirmPressed(event) + Keys.onEscapePressed: { + if (!detailView) { + event.accepted = false + return + } + + showProviderInformation(false) + } + + QtObject { + id: d + + function onKeyboardConfirmPressed(event) { + if (detailView) { + showProviderInformation(false) + } else { + confirmButton.onClicked() + } + } + } + Column { visible: !root.detailView - anchors.left: parent.left anchors.right: parent.right + spacing: Constants.pane_spacing topPadding: providerRect.height Rectangle { id: providerRect + height: providerColumn.height anchors.left: parent.left anchors.right: parent.right + color: Constants.white Column { id: providerColumn + anchors.left: parent.left anchors.right: parent.right anchors.margins: Constants.pane_padding + topPadding: Constants.pane_padding bottomPadding: Constants.pane_padding spacing: Constants.pane_spacing @@ -49,21 +86,33 @@ SectionPage { Image { id: providerImage - source: "qrc:///images/npa.svg" - sourceSize.height: providerText.height * 4 + anchors.left: parent.left anchors.bottom: parent.bottom + + source: "qrc:///images/npa.svg" + sourceSize.height: providerText.height * 4 } - Text { + GText { id: providerText + anchors.left: providerImage.right anchors.leftMargin: Constants.pane_spacing anchors.right: parent.right - color: Constants.black - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + + activeFocusOnTab: true + Accessible.role: Accessible.Heading + Accessible.name: providerText.text + + //: LABEL DESKTOP_QML + text: qsTr("You are about to identify yourself towards the following service provider:") + SettingsModel.translationTrigger + textStyle: Style.text.normal_inverse + + FocusFrame { + border.color: Constants.black + dynamic: false + } } } @@ -73,65 +122,116 @@ SectionPage { Row { id: providerRow + spacing: Constants.component_spacing ProviderInfoSection { + id: purposeInfo + + activeFocusOnTab: true + image: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + //: LABEL DESKTOP_QML + title: qsTr("Purpose for reading out requested data") + SettingsModel.translationTrigger name: certificateDescriptionModel.purpose } ProviderInfoSection { + id: providerInfo + + activeFocusOnTab: true + image: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger + //: LABEL DESKTOP_QML + title: qsTr("Service provider") + SettingsModel.translationTrigger name: certificateDescriptionModel.subjectName } } GButton { + id: moreButton + anchors.right: parent.right anchors.bottom: parent.bottom - text: qsTr("more...") + settingsModel.translationTrigger + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("more...") + SettingsModel.translationTrigger onClicked: showProviderInformation(true) } } } } - Text { - color: Constants.black - font.pixelSize: Constants.normal_font_size + GText { + id: dataIntroduction + anchors.left: parent.left anchors.right: parent.right anchors.margins: Constants.pane_padding - wrapMode: Text.WordWrap - text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + + activeFocusOnTab: true + Accessible.role: Accessible.Heading + Accessible.name: dataIntroduction.text + + //: LABEL DESKTOP_QML + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + SettingsModel.translationTrigger + textStyle: Style.text.normal_inverse + + FocusFrame { + dynamic: false + } } Pane { anchors.margins: Constants.pane_padding + anchors.left: parent.left + anchors.right: parent.right Column { id: transactionInfo + + visible: !!transactionText.text width: parent.width - visible: !!transactionInfoText.text + spacing: Constants.pane_spacing - Text { + GText { + id: transactionHeading + width: parent.width - text: qsTr("Transactional information") + settingsModel.translationTrigger - color: Constants.blue - font.pixelSize: Constants.pane_title_font_size - wrapMode: Text.WordWrap + + activeFocusOnTab: true + Accessible.role: Accessible.Section + Accessible.name: transactionHeading.text + + //: LABEL DESKTOP_QML + text: qsTr("Transactional information") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + + FocusFrame { + border.color: Constants.black + dynamic: false + } } - Text { - id: transactionInfoText + GText { + id: transactionText + width: parent.width + + activeFocusOnTab: true + Accessible.role: Accessible.Paragraph + Accessible.name: transactionText.text + text: AuthModel.transactionInfo - color: Constants.black - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap + textStyle: Style.text.normal_inverse + + FocusFrame { + border.color: Constants.black + dynamic: false + } } } @@ -142,9 +242,11 @@ SectionPage { DataGroup { id: requiredData + width: columns * parent.columnWidth + ((columns - 1) * Constants.pane_spacing) - title: qsTr("Required Data") + settingsModel.translationTrigger + //: LABEL DESKTOP_QML + title: qsTr("Required Data") + SettingsModel.translationTrigger columns: !optionalData.visible ? 3 : count > optionalData.count ? 2 : 1 @@ -153,18 +255,27 @@ SectionPage { DataGroup { id: optionalData + width: columns * parent.columnWidth + ((columns - 1) * Constants.pane_spacing) - title: qsTr("Optional Data") + settingsModel.translationTrigger + //: LABEL DESKTOP_QML + title: qsTr("Optional Data") + SettingsModel.translationTrigger columns: 3 - (requiredData.visible ? requiredData.columns : 0) chat: chatModel.optional } } GButton { - icon.source: "qrc:///images/npa.svg" + id: confirmButton + anchors.right: parent.right - text: qsTr("Identify now") + settingsModel.translationTrigger + + activeFocusOnTab: true + Accessible.name: confirmButton.text + + icon.source: "qrc:///images/npa.svg" + //: LABEL DESKTOP_QML %1 can be CAN or PIN + text: qsTr("Proceed to %1 entry").arg(NumberModel.isCanAllowedMode ? "CAN" : "PIN") + SettingsModel.translationTrigger onClicked: { chatModel.transferAccessRights() AuthModel.continueWorkflow() @@ -175,7 +286,9 @@ SectionPage { CertificateDescriptionPage { id: certificateDescriptionPage + visible: root.detailView + onExit: showProviderInformation(false) } } diff --git a/resources/qml/Governikus/IdentifyView/+desktop/IdentifyController.qml b/resources/qml/Governikus/IdentifyView/+desktop/IdentifyController.qml index 26b796e..2872610 100644 --- a/resources/qml/Governikus/IdentifyView/+desktop/IdentifyController.qml +++ b/resources/qml/Governikus/IdentifyView/+desktop/IdentifyController.qml @@ -1,17 +1,22 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 import Governikus.Type.AuthModel 1.0 +import Governikus.Type.NumberModel 1.0 +import Governikus.Type.CardReturnCode 1.0 -SectionPage { +Controller { enum WorkflowStates { Initial, Reader, Card, Update, - Can, - Pin, + Password, Processing } @@ -20,7 +25,6 @@ SectionPage { property bool connectedToCard: false property int workflowState: 0 - property int workflowProgressValue: 0 property bool workflowProgressVisible: false states: [ @@ -47,22 +51,22 @@ SectionPage { target: AuthModel onFireCurrentStateChanged: processStateChange() // This is necessary because onCurrentStateChanged is not - // working, when we need to process a state a second time + // working, when we need to process a state a second time. } function showRemoveCardFeedback() { if (controller.connectedToCard) { controller.connectedToCard = false - qmlExtension.showFeedback(qsTr("You may now remove your ID card from the device.")) + //: INFO DESKTOP_QML The authentication process is completed, the id card may be removed from the card reader. + ApplicationModel.showFeedback(qsTr("You may now remove your ID card from the device.")) } } function processStateChange() { switch (AuthModel.currentState) { - case "": + case "Initial": break; case "StateGetTcToken": - // enterPinView.state = "INITIAL" controller.workflowState = IdentifyController.WorkflowStates.Initial break case "StateEditAccessRights": @@ -77,57 +81,50 @@ SectionPage { setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Card) break case "StateHandleRetryCounter": - controller.nextView(IdentifyView.SubViews.Progress) setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Update) break - case "StateEstablishPaceCan": - setIdentifyWorkflowStateAndRequestInput(IdentifyController.WorkflowStates.Can, "CAN") + case "StateEnterPacePassword": + controller.workflowState = IdentifyController.WorkflowStates.Password + if (AuthModel.isBasicReader) { + controller.nextView(IdentifyView.SubViews.Password) + } else { + AuthModel.continueWorkflow() + } break - case "StateEstablishPacePin": - setIdentifyWorkflowStateAndRequestInput(IdentifyController.WorkflowStates.Pin, "PIN") + case "StateUnfortunateCardPosition": + controller.nextView(IdentifyView.SubViews.CardPosition) break - case "StateDidAuthenticateEac1": - controller.workflowProgressVisible = true - controller.workflowProgressValue = 1 + case "StateSendDIDAuthenticateResponseEAC1": + controller.nextView(IdentifyView.SubViews.Progress) + if (NumberModel.inputErrorCode == CardReturnCode.OK) { + controller.workflowProgressVisible = true + } setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Processing) break - case "StateEstablishPacePuk": - AuthModel.cancelWorkflowOnPinBlocked() - break - case "StateDidAuthenticateEac2": - controller.workflowProgressValue = 2 - AuthModel.continueWorkflow() - break - case "StateTransmit": - controller.workflowProgressValue = 3 - AuthModel.continueWorkflow() - break case "StateCleanUpReaderManager": controller.connectedToCard = AuthModel.selectedReaderHasCard() && !AuthModel.isError; AuthModel.continueWorkflow() break; - case "StateCheckRefreshAddress": - controller.workflowProgressValue = 4 - AuthModel.continueWorkflow() - break case "StateWriteHistory": showRemoveCardFeedback() - controller.workflowProgressValue = 5 AuthModel.continueWorkflow() break - case "StateShowSelfInfo": - controller.nextView(IdentifyView.SubViews.Data) - break case "FinalState": if (AuthModel.error) { - showRemoveCardFeedback() - controller.nextView(IdentifyView.SubViews.Result) + if (AuthModel.shouldSkipResultView()) { + controller.nextView(IdentifyView.SubViews.ReturnToMain) + AuthModel.continueWorkflow() + } else { + showRemoveCardFeedback() + controller.nextView(IdentifyView.SubViews.Result) + } + } else if (ApplicationModel.currentWorkflow === "selfauthentication") { + controller.nextView(IdentifyView.SubViews.Data) } else { - AuthModel.continueWorkflow() controller.nextView(IdentifyView.SubViews.ReturnToMain) + AuthModel.continueWorkflow() } controller.workflowProgressVisible = false - controller.workflowProgressValue = 0 break default: AuthModel.continueWorkflow() @@ -138,14 +135,4 @@ SectionPage { controller.workflowState = pState AuthModel.continueWorkflow() } - - function setIdentifyWorkflowStateAndRequestInput(pState, pInput) { - controller.workflowState = pState - if (AuthModel.isBasicReader) { - // enterPinView.state = pInput - controller.nextView(IdentifyView.SubViews.Password) - } else { - AuthModel.continueWorkflow() - } - } } diff --git a/resources/qml/Governikus/IdentifyView/+desktop/IdentifyView.qml b/resources/qml/Governikus/IdentifyView/+desktop/IdentifyView.qml index c9680fa..7ae3165 100644 --- a/resources/qml/Governikus/IdentifyView/+desktop/IdentifyView.qml +++ b/resources/qml/Governikus/IdentifyView/+desktop/IdentifyView.qml @@ -1,50 +1,76 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 -import Governikus.EnterPinView 1.0 +import Governikus.EnterPasswordView 1.0 import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 +import Governikus.Style 1.0 import Governikus.ProgressView 1.0 import Governikus.ResultView 1.0 +import Governikus.SettingsView 1.0 +import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Workflow 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ChangePinModel 1.0 import Governikus.Type.AuthModel 1.0 import Governikus.Type.NumberModel 1.0 +import Governikus.Type.CardReturnCode 1.0 SectionPage { + id: identifyView + enum SubViews { - Connectivity = 1, + Undefined, + Connectivity, Progress, AccessRights, Workflow, Password, + PasswordInfo, + PasswordLength, + CardPosition, + InputError, Data, Result, - ReturnToMain + ReturnToMain, + ReaderSettings } - id: identifyView - - KeyNavigation.tab: visibleChildren.length > 1 ? visibleChildren[1] : navSuccessor - - Accessible.role: Accessible.Grouping - Accessible.name: qsTr("Identify view") + settingsModel.translationTrigger - Accessible.description: qsTr("This is the identify view of the AusweisApp2.") + settingsModel.translationTrigger - - readonly property var tabTarget: visibleChildren.length > 1 ? visibleChildren[1] : identifyView - - property int activeView: 0 + activeFocusOnTab: true + Keys.onEscapePressed: if (d.cancelAllowed) AuthModel.cancelWorkflow() titleBarAction: TitleBarAction { - text: qsTr("Identify") - showSettings: false - showHelp: false + //: LABEL DESKTOP_QML + text: qsTr("Identify") + SettingsModel.translationTrigger + rootEnabled: false + helpTopic: "ausweisenPage" + showSettings: (identifyController.workflowState === IdentifyController.WorkflowStates.Initial || + identifyController.workflowState === IdentifyController.WorkflowStates.Reader || + identifyController.workflowState === IdentifyController.WorkflowStates.Card) + && d.activeView !== IdentifyView.SubViews.Progress - onClicked: editRights.showProviderInformation(false) + onClicked: { + editRights.showProviderInformation(false) + if (d.activeView === IdentifyView.SubViews.PasswordInfo || d.activeView === IdentifyView.SubViews.PasswordLength) { + d.view = IdentifyView.SubViews.Password + ApplicationWindow.menuBar.updateActions() + } + else if (d.activeView === IdentifyView.SubViews.ReaderSettings) { + d.view = readerView.preceedingView + ApplicationWindow.menuBar.updateActions() + } + } customSubAction: CancelAction { + visible: d.cancelAllowed + onClicked: { if (identifyResult.visible) { AuthModel.continueWorkflow() @@ -54,113 +80,225 @@ SectionPage } } } + + customSettingsHandler: function(){ + readerView.preceedingView = d.activeView + d.view = IdentifyView.SubViews.ReaderSettings + ApplicationWindow.menuBar.updateActions() + } } -/* - content: IdentifyViewInfo { - id: identifyViewInfo - width: identifyEditChatView.width - height: identifyEditChatView.height + + QtObject { + id: d + + property int view: IdentifyView.SubViews.Undefined + readonly property int activeView: inputError.visible ? IdentifyView.SubViews.InputError : view + readonly property bool cancelAllowed: AuthModel.isBasicReader || generalWorkflow.waitingFor != Workflow.WaitingFor.Password } -*/ + + TabbedReaderView { + id: readerView + + property int preceedingView: IdentifyView.SubViews.Undefined + + visible: d.activeView === IdentifyView.SubViews.ReaderSettings + onCloseView: { + d.view = preceedingView + ApplicationWindow.menuBar.updateActions() + } + } + IdentifyController { id: identifyController onNextView: { - if (pName < 1) { - console.warn("Unknown view requested: " + pName) - return; - } - if (pName === IdentifyView.SubViews.ReturnToMain) { identifyView.nextView(SectionPage.Views.Main) return; } - identifyView.activeView = pName + d.view = pName } } + ProgressView { + id: checkConnectivityView + + visible: d.activeView === IdentifyView.SubViews.Connectivity + + //: INFO DESKTOP_QML Header of the message that no network connection is present during the authentication procedure. + text: qsTr("No network connectivity") + SettingsModel.translationTrigger + //: INFO DESKTOP_QML Content of the message that no network connection is present during the authentication procedure. + subText: qsTr("Please enable the network interface or cancel the workflow.") + SettingsModel.translationTrigger + subTextColor: Constants.red + } + EditRights { id: editRights - visible: identifyView.activeView === IdentifyView.SubViews.AccessRights + + visible: d.activeView === IdentifyView.SubViews.AccessRights } - SelfAuthenticationData { - visible: identifyView.activeView === IdentifyView.SubViews.Data - onVisibleChanged: ApplicationWindow.menuBar.updateActions() - } -/* - IdentifyWorkflow { - id: identifyWorkflow - visible: false - } + GeneralWorkflow { + id: generalWorkflow - EnterPinView { - id: enterPinView - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: AuthModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger } - visible: false + visible: d.activeView === IdentifyView.SubViews.Workflow - onPinEntered: { - firePop() - AuthModel.continueWorkflow() + isPinChange: false + waitingFor: switch (identifyController.workflowState) { + case IdentifyController.WorkflowStates.Reader: + return Workflow.WaitingFor.Reader + case IdentifyController.WorkflowStates.Card: + return Workflow.WaitingFor.Card + case IdentifyController.WorkflowStates.Password: + return Workflow.WaitingFor.Password + default: + return Workflow.WaitingFor.None } } -*/ + + EnterPasswordView { + id: enterPasswordView + + visible: d.activeView === IdentifyView.SubViews.Password + + onPasswordEntered: { + d.view = IdentifyView.SubViews.Progress + AuthModel.continueWorkflow() + } + + onChangePinLength: { + d.view = IdentifyView.SubViews.PasswordLength + ApplicationWindow.menuBar.updateActions() + } + + onRequestPasswordInfo: { + d.view = IdentifyView.SubViews.PasswordInfo + ApplicationWindow.menuBar.updateActions() + } + } + + PasswordInfoView { + id: passwordInfoView + + visible: d.activeView === IdentifyView.SubViews.PasswordInfo + + onClose: { + d.view = IdentifyView.SubViews.Password + ApplicationWindow.menuBar.updateActions() + } + } + + ResultView { + id: passwordLengthView + + visible: d.activeView === IdentifyView.SubViews.PasswordLength + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Transport PIN") + SettingsModel.translationTrigger + rootEnabled: false + helpTopic: "pinTab" + customSubAction: CancelAction { + onClicked: { + d.view = IdentifyView.SubViews.Password + ApplicationWindow.menuBar.updateActions() + } + } + } + resultType: ResultView.Type.IsInfo + //: INFO DESKTOP_QML The user clicked that the current PIN has 5 digits (transport PIN) which needs to be changed to a 6-digit PIN. The current process will be aborted and needs to be restarted *manually* by the user. + text: qsTr("You have to change your transport PIN into a personal PIN to use the online ID function. You are currently leaving the started process and are forwarded to the PIN management. Please restart the desired process after the PIN has been changed.") + SettingsModel.translationTrigger + onNextView: { + ChangePinModel.startWorkflow() + AuthModel.cancelWorkflowToChangePin() + } + } + + ResultView { + id: inputError + + property bool errorConfirmed: false + + visible: !errorConfirmed && NumberModel.hasPasswordError && d.view != IdentifyView.SubViews.Result + + resultType: ResultView.Type.IsError + text: NumberModel.inputError + onNextView: errorConfirmed = true + + Connections { + target: NumberModel + onFireInputErrorChanged: inputError.errorConfirmed = false + } + } + + ResultView { + id: cardPositionView + + visible: d.activeView === IdentifyView.SubViews.CardPosition + + resultType: ResultView.Type.IsInfo + //: INFO DESKTOP_QML A weak NFC signal was detected since the card communication was aborted. The card's position needs to be adjusted to hopefully achieve better signal strength. + text: qsTr("Weak NFC signal.\nPlease reposition your card.") + SettingsModel.translationTrigger + onNextView: AuthModel.continueWorkflow() + } ProgressView { id: identifyProgressView readonly property bool inProgress: identifyController.workflowState !== IdentifyController.WorkflowStates.Initial - visible: identifyView.activeView === IdentifyView.SubViews.Progress - text: (inProgress ? qsTr("Authentication in progress") : qsTr("Acquiring provider certificate")) + settingsModel.translationTrigger + visible: d.activeView === IdentifyView.SubViews.Progress + + //: INFO DESKTOP_QML Header of the progress information during the authentication process. + text: (inProgress ? qsTr("Authentication in progress") : qsTr("Acquiring provider certificate")) + SettingsModel.translationTrigger subText: { - settingsModel.translationTrigger; + SettingsModel.translationTrigger; if (!visible) { return "" } if (AuthModel.isBasicReader) { + //: INFO DESKTOP_QML Second line text if a basic card reader is used and data is exchanged with the card/server in the background. Is not actually visible since the basic reader password handling is done by EnterPasswordView. return qsTr("Please wait a moment...") } if (!!NumberModel.inputError) { return NumberModel.inputError } if (NumberModel.pinDeactivated) { + //: INFO DESKTOP_QML The online authentication feature of the id card is deactivated and needs to be activated by the local authorities. return qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") } + //: INFO DESKTOP_QML Generic progress status message during authentication. return qsTr("Please wait a moment...") } - subTextColor: !AuthModel.isBasicReader && (NumberModel.inputError || NumberModel.pinDeactivated) ? "red" : Constants.secondary_text - progressValue: identifyController.workflowProgressValue - progressText: (progressValue === 0 ? "" : - progressValue === 1 ? qsTr("Service provider is being verified") : - progressValue === 2 ? qsTr("Card is being verified") : - progressValue === 3 ? qsTr("Reading data") : - progressValue === 4 ? qsTr("Sending data to service provider") : - progressValue === 5 ? qsTr("Preparing results") : - "") + settingsModel.translationTrigger + subTextColor: !AuthModel.isBasicReader && (NumberModel.inputError || NumberModel.pinDeactivated) ? Style.color.warning_text : Style.color.secondary_text + progressValue: AuthModel.progressValue + progressText: AuthModel.progressMessage progressBarVisible: identifyController.workflowProgressVisible } - ProgressView { - id: checkConnectivityView - visible: identifyView.activeView === IdentifyView.SubViews.Connectivity - text: qsTr("No network connectivity") + settingsModel.translationTrigger - subText: qsTr("Please enable the network interface or cancel the workflow.") + settingsModel.translationTrigger - subTextColor: Constants.red + SelfAuthenticationData { + visible: d.activeView === IdentifyView.SubViews.Data + + onVisibleChanged: ApplicationWindow.menuBar.updateActions() + onNextView: { + identifyView.nextView(pName) + AuthModel.continueWorkflow() + } } ResultView { id: identifyResult - navSuccessor: identifyView.navSuccessor - isError: AuthModel.resultString + visible: d.activeView === IdentifyView.SubViews.Result + + resultType: AuthModel.resultString ? ResultView.Type.IsError : ResultView.Type.IsSuccess text: AuthModel.resultString onNextView: { - AuthModel.continueWorkflow() identifyView.nextView(pName) + AuthModel.continueWorkflow() } - visible: identifyView.activeView === IdentifyView.SubViews.Result + emailButtonVisible: AuthModel.resultString + onEmailButtonPressed: AuthModel.sendResultMail() } } diff --git a/resources/qml/Governikus/IdentifyView/+desktop/SelfAuthenticationData.qml b/resources/qml/Governikus/IdentifyView/+desktop/SelfAuthenticationData.qml index bf57822..990b75f 100644 --- a/resources/qml/Governikus/IdentifyView/+desktop/SelfAuthenticationData.qml +++ b/resources/qml/Governikus/IdentifyView/+desktop/SelfAuthenticationData.qml @@ -1,73 +1,118 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 -import Governikus.Type.AuthModel 1.0 - import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.AuthModel 1.0 +import Governikus.Type.SelfAuthModel 1.0 SectionPage { + id: baseItem + + Accessible.name: qsTr("Self-authentication data view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the self-authentication data view of the AusweisApp2.") + SettingsModel.translationTrigger + Keys.onReturnPressed: okButton.onClicked() + Keys.onEnterPressed: okButton.onClicked() + Keys.onEscapePressed: okButton.onClicked() + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML text: qsTr("Read data") - showSettings: false + rootEnabled: false showHelp: false } Row { id: statusRow + height: parent.height / 4 anchors.top: parent.top anchors.topMargin: Constants.component_spacing anchors.horizontalCenter: parent.horizontalCenter - height: parent.height / 4 + spacing: Constants.component_spacing StatusIcon { - height: ApplicationModel.scaleFactor * 200 + height: Style.dimens.status_icon_medium anchors.verticalCenter: parent.verticalCenter + source: "qrc:///images/status_ok.svg" } - Text { + GText { id: successText + anchors.verticalCenter: parent.verticalCenter - text: qsTr("Successfull reading data") + settingsModel.translationTrigger - font.pixelSize: Constants.header_font_size - color: Constants.white + + activeFocusOnTab: true + Accessible.name: successText.text + + //: INFO DESKTOP_QML Status message that the self authentication successfully completed. + text: qsTr("Successfully read data") + SettingsModel.translationTrigger + textStyle: Style.text.header + + FocusFrame {} } } Pane { id: pane + anchors.top: statusRow.bottom + anchors.left: parent.left + anchors.right: parent.right anchors.margins: Constants.pane_padding - title: qsTr("Read data") + settingsModel.translationTrigger + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + title: qsTr("Read data") + SettingsModel.translationTrigger Grid { id: grid + width: parent.width + columns: 3 - spacing: Utils.dp(15) + spacing: Constants.groupbox_spacing verticalItemAlignment: Grid.AlignTop + Repeater { - model: selfAuthModel + id: dataRepeater + model: SelfAuthModel LabeledText { + width: (pane.width - 2 * Constants.pane_padding - (grid.columns - 1) * grid.spacing) / grid.columns + + activeFocusOnTab: true + label: name text: value === "" ? "---" : value - width: (pane.width - 2 * Constants.pane_padding - (grid.columns - 1) * grid.spacing) / grid.columns } } } GButton { id: okButton + anchors.right: parent.right - text: qsTr("OK") + settingsModel.translationTrigger - onClicked: AuthModel.continueWorkflow() + + activeFocusOnTab: true + Accessible.name: okButton.text + + //: LABEL DESKTOP_QML + text: qsTr("OK") + SettingsModel.translationTrigger + onClicked: baseItem.nextView(SectionPage.Views.Main) } } } diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+android/+phone/EditRights.qml b/resources/qml/Governikus/IdentifyView/+mobile/+android/+phone/EditRights.qml index 39a0479..da2ee63 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/+android/+phone/EditRights.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/+android/+phone/EditRights.qml @@ -1,19 +1,29 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtGraphicalEffects 1.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 import Governikus.Type.AuthModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.NumberModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction { + navigationAction: NavigationAction { state: "cancel" onClicked: AuthModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID_PHONE + title: qsTr("Identify") + SettingsModel.translationTrigger content: Column { width: baseItem.width @@ -23,12 +33,10 @@ SectionPage { width: parent.width - 2 * Constants.pane_padding spacing: Constants.component_spacing - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { width: parent.width - wrapMode: Text.WordWrap - text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + //: LABEL ANDROID_PHONE + text: qsTr("You are about to identify yourself towards the following service provider:") + SettingsModel.translationTrigger } Pane { @@ -46,24 +54,32 @@ SectionPage { ProviderInfoSection { imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger + //: LABEL ANDROID_PHONE + title: qsTr("Service provider") + SettingsModel.translationTrigger name: certificateDescriptionModel.subjectName } ProviderInfoSection { imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + //: LABEL ANDROID_PHONE + title: qsTr("Purpose for reading out requested data") + SettingsModel.translationTrigger name: certificateDescriptionModel.purpose } } - Text { + Image { id: forwardAction anchors.right: parent.right anchors.verticalCenter: providerEntries.verticalCenter - text: ">" - font.pixelSize: Utils.dp(22) - color: Constants.grey + sourceSize.height: Style.dimens.small_icon_size + fillMode: Image.PreserveAspectFit + source: "qrc:///images/arrowRight.svg" + + ColorOverlay { + anchors.fill: forwardAction + source: forwardAction + color: Style.color.secondary_text + } } MouseArea { @@ -82,19 +98,19 @@ SectionPage { GButton { iconSource: "qrc:///images/npa.svg" anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Identify now") + settingsModel.translationTrigger; + //: LABEL ANDROID_PHONE %1 can be CAN or PIN + text: qsTr("Proceed to %1 entry").arg(NumberModel.isCanAllowedMode ? "CAN" : "PIN") + SettingsModel.translationTrigger onClicked: { chatModel.transferAccessRights() AuthModel.continueWorkflow() } } - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { width: parent.width - wrapMode: Text.WordWrap - text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + + //: LABEL ANDROID_PHONE + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + SettingsModel.translationTrigger } Pane { @@ -109,27 +125,29 @@ SectionPage { PaneTitle { height: implicitHeight * 1.5 verticalAlignment: Text.AlignTop - text: qsTr("Transactional information") + settingsModel.translationTrigger + //: LABEL ANDROID_PHONE + text: qsTr("Transactional information") + SettingsModel.translationTrigger } - Text { + GText { id: transactionInfoText - color: Constants.secondary_text width: parent.width - font.pixelSize: Constants.normal_font_size + text: AuthModel.transactionInfo - wrapMode: Text.WordWrap + textStyle: Style.text.normal } } DataGroup { - title: qsTr("Required Data") + settingsModel.translationTrigger + //: LABEL ANDROID_PHONE + title: qsTr("Required Data") + SettingsModel.translationTrigger chat: chatModel.required } DataGroup { - title: qsTr("Optional Data") + settingsModel.translationTrigger + //: LABEL ANDROID_PHONE + title: qsTr("Optional Data") + SettingsModel.translationTrigger chat: chatModel.optional } } diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+android/+tablet/EditRights.qml b/resources/qml/Governikus/IdentifyView/+mobile/+android/+tablet/EditRights.qml index c11317d..2aea769 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/+android/+tablet/EditRights.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/+android/+tablet/EditRights.qml @@ -1,19 +1,28 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 import Governikus.Type.AuthModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.NumberModel 1.0 + SectionPage { id: root - leftTitleBarAction: TitleBarAction { + navigationAction: NavigationAction { state: "cancel" onClicked: AuthModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID_TABLET + title: qsTr("Identify") + SettingsModel.translationTrigger content: Column { width: baseItem.width @@ -23,12 +32,11 @@ SectionPage { width: parent.width - 2 * Constants.pane_padding spacing: Constants.pane_spacing - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { width: parent.width - wrapMode: Text.WordWrap - text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + + //: LABEL ANDROID_TABLET + text: qsTr("You are about to identify yourself towards the following service provider:") + SettingsModel.translationTrigger } Pane { @@ -51,12 +59,14 @@ SectionPage { ProviderInfoSection { imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + title: qsTr("Service provider") + SettingsModel.translationTrigger name: certificateDescriptionModel.subjectName } ProviderInfoSection { imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + title: qsTr("Purpose for reading out requested data") + SettingsModel.translationTrigger name: certificateDescriptionModel.purpose } } @@ -83,7 +93,8 @@ SectionPage { iconSource: "qrc:///images/npa.svg" anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - text: qsTr("Identify now") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET %1 can be CAN or PIN + text: qsTr("Proceed to %1 entry").arg(NumberModel.isCanAllowedMode ? "CAN" : "PIN") + SettingsModel.translationTrigger onClicked: { chatModel.transferAccessRights() AuthModel.continueWorkflow() @@ -93,19 +104,18 @@ SectionPage { } } - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { width: parent.width - wrapMode: Text.WordWrap - text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + + //: LABEL ANDROID_TABLET + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + SettingsModel.translationTrigger } Pane { Column { height: childrenRect.height width: parent.width - spacing: Utils.dp(30) + spacing: 30 Column { id: transactionInfo @@ -116,17 +126,17 @@ SectionPage { PaneTitle { height: implicitHeight * 1.5 verticalAlignment: Text.AlignTop - text: qsTr("Transactional information") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + text: qsTr("Transactional information") + SettingsModel.translationTrigger } - Text { + GText { id: transactionInfoText - color: Constants.secondary_text width: parent.width - font.pixelSize: Constants.normal_font_size + text: AuthModel.transactionInfo - wrapMode: Text.WordWrap + textStyle: Style.text.normal_secondary } } @@ -138,7 +148,8 @@ SectionPage { id: requiredData width: optionalData.visible ? parent.width * 0.63 : parent.width - title: qsTr("Required Data") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + title: qsTr("Required Data") + SettingsModel.translationTrigger columns: optionalData.visible ? 2 : 3 chat: chatModel.required } @@ -147,7 +158,8 @@ SectionPage { id: optionalData width: parent.width * 0.37 - Constants.pane_spacing - title: qsTr("Optional Data") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + title: qsTr("Optional Data") + SettingsModel.translationTrigger chat: chatModel.optional } } diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+android/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+mobile/+android/DataGroup.qml index 54f74b0..35e9649 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/+android/DataGroup.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/+android/DataGroup.qml @@ -1,7 +1,13 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 Rectangle { @@ -28,22 +34,22 @@ Rectangle { Rectangle { width: parent.width - height: Utils.dp(40) + height: 40 visible: repeater.count < 1 - Text { + GText { id: emptyText - color: Constants.secondary_text + anchors.verticalCenter: parent.verticalCenter width: parent.width - font.pixelSize: Constants.normal_font_size - text: qsTr("No data requested") + settingsModel.translationTrigger + + //: LABEL ANDROID + text: qsTr("No data requested") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } - Rectangle { + GSeparator { anchors.top: parent.bottom anchors.topMargin: -height - height: 1 width: parent.width - color: Constants.grey } } @@ -61,26 +67,25 @@ Rectangle { Rectangle { width: (grid.width - ((grid.columns - 1) * grid.columnSpacing)) / grid.columns - height: Utils.dp(40) - color: "white" + height: 40 + color: Constants.white - Text { + GText { id: text - color: Constants.secondary_text + anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.right: checkBox.left - font.pixelSize: Constants.normal_font_size + text: name - wrapMode: Text.WordWrap + textStyle: Style.text.normal_secondary } - Rectangle { + GSeparator { anchors.top: parent.bottom anchors.topMargin: -height - height: 1 width: parent.width - color: Constants.grey + visible: index < repeater.count - 1 } GCheckBox { @@ -99,7 +104,7 @@ Rectangle { anchors.centerIn: parent width: root.width height: parent.height - color: Constants.accent_color + color: Style.color.accent opacity: parent.pressed ? 0.5 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } } diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/DataGroup.qml index 5005d4a..17d16f3 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/DataGroup.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/DataGroup.qml @@ -1,42 +1,62 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 Pane { + id: pane + + signal scrollPageUp() + signal scrollPageDown() + property alias chat: repeater.model - id: pane - spacing: 0 visible: repeater.count > 0 + contentSpacing: 0 + Repeater { id: repeater Rectangle { width: parent.width - height: Utils.dp(40) + height: 40 + + Accessible.role: Accessible.ListItem + Accessible.name: name + Accessible.checkable: true + Accessible.checked: checkBox.checked + Accessible.onPressAction: if (optional) selected = !selected + Accessible.onScrollDownAction: baseItem.scrollPageDown() + Accessible.onScrollUpAction: baseItem.scrollPageUp() + radius: 3 - color: "white" - Text { + color: Constants.white + + GText { id: dataGroup - color: Constants.secondary_text + anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter anchors.right: checkBox.left width: parent.width - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap + + Accessible.ignored: true + text: name + textStyle: Style.text.normal_secondary } - Rectangle { + GSeparator { anchors.top: parent.bottom anchors.topMargin: -height - height: 1 anchors.left: dataGroup.left anchors.right: dataGroup.right - color: Constants.grey } GCheckBox { id: checkBox diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/EditRights.qml b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/EditRights.qml index e6870f5..632cc1a 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/EditRights.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+phone/EditRights.qml @@ -1,35 +1,45 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtGraphicalEffects 1.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 import Governikus.Type.AuthModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.NumberModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction { + navigationAction: NavigationAction { state: "cancel" onClicked: AuthModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + //: LABEL IOS_PHONE + title: qsTr("Identify") + SettingsModel.translationTrigger content: Column { - width: baseItem.width - padding: Constants.pane_padding + width: baseItem.width + padding: Constants.pane_padding - Column { - width: parent.width - 2 * Constants.pane_padding + Column { + width: parent.width - 2 * Constants.pane_padding spacing: Constants.component_spacing - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { width: parent.width - wrapMode: Text.WordWrap - text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + + //: LABEL IOS_PHONE + text: qsTr("You are about to identify yourself towards the following service provider:") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } Pane { @@ -38,6 +48,9 @@ SectionPage { width: parent.width height: providerEntries.height + Accessible.description: qsTr("Click for more information about the service provider") + SettingsModel.translationTrigger + Accessible.onPressAction: mouseArea.clicked(null) + Column { id: providerEntries anchors.top: parent.top @@ -47,28 +60,39 @@ SectionPage { ProviderInfoSection { imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger + //: LABEL IOS_PHONE + title: qsTr("Service provider") + SettingsModel.translationTrigger name: certificateDescriptionModel.subjectName } ProviderInfoSection { imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + //: LABEL IOS_PHONE + title: qsTr("Purpose for reading out requested data") + SettingsModel.translationTrigger name: certificateDescriptionModel.purpose } } - Text { + Image { id: forwardAction anchors.right: parent.right anchors.verticalCenter: providerEntries.verticalCenter - text: ">" - font.pixelSize: Utils.dp(22) - color: Constants.grey + sourceSize.height: Style.dimens.small_icon_size + fillMode: Image.PreserveAspectFit + source: "qrc:///images/arrowRight.svg" + + ColorOverlay { + anchors.fill: forwardAction + source: forwardAction + color: Style.color.secondary_text + } } MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: firePush(certificateDescriptionPage) } @@ -83,44 +107,53 @@ SectionPage { GButton { iconSource: "qrc:///images/npa.svg" anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Identify now") + settingsModel.translationTrigger; + //: LABEL IOS_PHONE %1 can be CAN or PIN + text: qsTr("Proceed to %1 entry").arg(NumberModel.isCanAllowedMode ? "CAN" : "PIN") + SettingsModel.translationTrigger onClicked: { chatModel.transferAccessRights() AuthModel.continueWorkflow() } } - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { width: parent.width - wrapMode: Text.WordWrap - text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + + //: LABEL IOS_PHONE + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } Pane { id: transactionInfo - title: qsTr("Transactional information") + settingsModel.translationTrigger + //: LABEL IOS_PHONE + title: qsTr("Transactional information") + SettingsModel.translationTrigger visible: !!transactionInfoText.text - Text { + GText { id: transactionInfoText - color: Constants.secondary_text width: parent.width - font.pixelSize: Constants.normal_font_size + text: AuthModel.transactionInfo - wrapMode: Text.WordWrap + textStyle: Style.text.normal_secondary } } DataGroup { - title: qsTr("Required Data") + settingsModel.translationTrigger + onScrollPageDown: baseItem.scrollPageDown() + onScrollPageUp: baseItem.scrollPageUp() + + //: LABEL IOS_PHONE + title: qsTr("Required Data") + SettingsModel.translationTrigger chat: chatModel.required } DataGroup { - title: qsTr("Optional Data") + settingsModel.translationTrigger + onScrollPageDown: baseItem.scrollPageDown() + onScrollPageUp: baseItem.scrollPageUp() + + //: LABEL IOS_PHONE + title: qsTr("Optional Data") + SettingsModel.translationTrigger chat: chatModel.optional } } diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/DataGroup.qml b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/DataGroup.qml index 98c7ac7..44d5211 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/DataGroup.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/DataGroup.qml @@ -1,11 +1,21 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 Rectangle { id: root + + signal scrollPageUp() + signal scrollPageDown() + property string title; property int columns: 1 property var chat @@ -28,22 +38,22 @@ Rectangle { Rectangle { width: parent.width - height: Utils.dp(40) + height: 40 visible: repeater.count < 1 - Text { - color: Constants.secondary_text + GText { id: emptyText + anchors.verticalCenter: parent.verticalCenter width: parent.width - font.pixelSize: Constants.normal_font_size - text: qsTr("No data requested") + settingsModel.translationTrigger + + //: LABEL IOS_TABLET + text: qsTr("No data requested") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } - Rectangle { + GSeparator { anchors.top: parent.bottom anchors.topMargin: -height - height: 1 width: parent.width - color: Constants.grey } } @@ -61,26 +71,27 @@ Rectangle { Rectangle { width: (grid.width - ((grid.columns - 1) * grid.columnSpacing)) / grid.columns - height: Utils.dp(40) - color: "white" + height: 40 + color: Constants.white - Text { + GText { id: text - color: Constants.secondary_text + + Accessible.onScrollDownAction: baseItem.scrollPageDown() + Accessible.onScrollUpAction: baseItem.scrollPageUp() + anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.right: checkBox.left - font.pixelSize: Constants.normal_font_size + text: name - wrapMode: Text.WordWrap + textStyle: Style.text.normal_secondary } - Rectangle { + GSeparator { anchors.top: parent.bottom anchors.topMargin: -height - height: 1 width: parent.width - color: Constants.grey } GCheckBox { @@ -99,7 +110,7 @@ Rectangle { anchors.centerIn: parent width: root.width height: parent.height - color: Constants.accent_color + color: Style.color.accent opacity: parent.pressed ? 0.5 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } } diff --git a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/EditRights.qml b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/EditRights.qml index d28d8ca..7eb8e69 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/EditRights.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/+ios/+tablet/EditRights.qml @@ -1,19 +1,29 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtGraphicalEffects 1.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 import Governikus.Type.AuthModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.NumberModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction { + navigationAction: NavigationAction { state: "cancel" onClicked: AuthModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + //: LABEL IOS_TABLET + title: qsTr("Identify") + SettingsModel.translationTrigger content: Column { width: baseItem.width @@ -21,14 +31,13 @@ SectionPage { Column { width: parent.width - 2 * Constants.pane_padding - spacing: Constants.component_spacing + spacing: Constants.component_spacing - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { width: parent.width - wrapMode: Text.WordWrap - text: qsTr("You are about to identify yourself towards the following service provider:") + settingsModel.translationTrigger + + //: LABEL IOS_TABLET + text: qsTr("You are about to identify yourself towards the following service provider:") + SettingsModel.translationTrigger } Pane { @@ -37,6 +46,9 @@ SectionPage { width: parent.width height: providerEntries.height + Accessible.description: qsTr("Click for more information about the service provider") + SettingsModel.translationTrigger + Accessible.onPressAction: mouseArea.clicked(null) + Column { id: providerEntries anchors.top: parent.top @@ -46,28 +58,39 @@ SectionPage { ProviderInfoSection { imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger + //: LABEL IOS_TABLET + title: qsTr("Service provider") + SettingsModel.translationTrigger name: certificateDescriptionModel.subjectName } ProviderInfoSection { imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + //: LABEL IOS_TABLET + title: qsTr("Purpose for reading out requested data") + SettingsModel.translationTrigger name: certificateDescriptionModel.purpose } } - Text { + Image { id: forwardAction anchors.right: parent.right anchors.verticalCenter: providerEntries.verticalCenter - text: ">" - font.pixelSize: Utils.dp(22) - color: Constants.grey + sourceSize.height: Style.dimens.small_icon_size + fillMode: Image.PreserveAspectFit + source: "qrc:///images/arrowRight.svg" + + ColorOverlay { + anchors.fill: forwardAction + source: forwardAction + color: Style.color.secondary_text + } } MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: firePush(certificateDescriptionPage) } @@ -82,26 +105,26 @@ SectionPage { GButton { iconSource: "qrc:///images/npa.svg" anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Identify now") + settingsModel.translationTrigger; + //: LABEL IOS_TABLET %1 can be CAN or PIN + text: qsTr("Proceed to %1 entry").arg(NumberModel.isCanAllowedMode ? "CAN" : "PIN") + SettingsModel.translationTrigger onClicked: { chatModel.transferAccessRights() AuthModel.continueWorkflow() } } - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { width: parent.width - wrapMode: Text.WordWrap - text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + settingsModel.translationTrigger + + //: LABEL IOS_TABLET + text: qsTr("The following data will be transferred to the service provider when you enter the PIN:") + SettingsModel.translationTrigger } Pane { Column { height: childrenRect.height width: parent.width - spacing: Utils.dp(30) + spacing: 30 Column { id: transactionInfo @@ -112,17 +135,17 @@ SectionPage { PaneTitle { height: implicitHeight * 1.5 verticalAlignment: Text.AlignTop - text: qsTr("Transactional information") + settingsModel.translationTrigger + //: LABEL IOS_TABLET + text: qsTr("Transactional information") + SettingsModel.translationTrigger } - Text { + GText { id: transactionInfoText - color: Constants.secondary_text width: parent.width - font.pixelSize: Constants.normal_font_size + text: AuthModel.transactionInfo - wrapMode: Text.WordWrap + textStyle: Style.text.normal_secondary } } @@ -132,9 +155,14 @@ SectionPage { DataGroup { id: requiredData + width: optionalData.visible ? parent.width * 0.63 : parent.width - title: qsTr("Required Data") + settingsModel.translationTrigger + onScrollPageDown: baseItem.scrollPageDown() + onScrollPageUp: baseItem.scrollPageUp() + + //: LABEL IOS_TABLET + title: qsTr("Required Data") + SettingsModel.translationTrigger columns: optionalData.visible ? 2 : 3 chat: chatModel.required } @@ -143,7 +171,11 @@ SectionPage { id: optionalData width: parent.width * 0.37 - Constants.pane_spacing - title: qsTr("Optional Data") + settingsModel.translationTrigger + onScrollPageDown: baseItem.scrollPageDown() + onScrollPageUp: baseItem.scrollPageUp() + + //: LABEL IOS_TABLET + title: qsTr("Optional Data") + SettingsModel.translationTrigger chat: chatModel.optional } } diff --git a/resources/qml/Governikus/IdentifyView/+mobile/CertificateDescriptionPage.qml b/resources/qml/Governikus/IdentifyView/+mobile/CertificateDescriptionPage.qml index de7446d..8db3d80 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/CertificateDescriptionPage.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/CertificateDescriptionPage.qml @@ -1,14 +1,20 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: root - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: name; font.bold: true } + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + title: name property string name @@ -24,7 +30,8 @@ SectionPage Pane { id: pane - title: qsTr("Provider Information") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + title: qsTr("Provider Information") + SettingsModel.translationTrigger Repeater { id: listView diff --git a/resources/qml/Governikus/IdentifyView/+mobile/IdentifyController.qml b/resources/qml/Governikus/IdentifyView/+mobile/IdentifyController.qml index 15c875f..b75992d 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/IdentifyController.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/IdentifyController.qml @@ -1,10 +1,15 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.AuthModel 1.0 import Governikus.Type.NumberModel 1.0 import Governikus.Type.ReaderPlugIn 1.0 -import Governikus.Type.PacePasswordId 1.0 + Item { enum WorkflowStates { @@ -28,7 +33,6 @@ Item { property bool connectedToCard: false property int workflowState: 0 - property int workflowProgressValue: 0 property bool workflowProgressVisible: false function sufficientBluetoothRights() { @@ -80,13 +84,20 @@ Item { function showRemoveCardFeedback() { if (controller.connectedToCard) { controller.connectedToCard = false - qmlExtension.showFeedback(qsTr("You may now remove your ID card from the device.")) + + // The feedback notification will crash Apple's VoiceOver if it happens at the same time the app is redirecting back to the browser. This + // happens with both the iOS toasts and our own toast-like replacement. To work around this, we'll only show the notification during a + // self-authentication on iOS with VoiceOver running: + if (Qt.platform.os !== "ios" || !ApplicationModel.isScreenReaderRunning() || ApplicationModel.currentWorkflow === "selfauthentication") { + //: INFO ANDROID IOS The authentication process is completed, the id card may (and should) be removed from the card reader. + ApplicationModel.showFeedback(qsTr("You may now remove your ID card from the device.")) + } } } function processStateChange() { switch (AuthModel.currentState) { - case "": + case "Initial": break; case "StateGetTcToken": enterPinView.state = "INITIAL" @@ -118,54 +129,42 @@ Item { setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Update) break case "StateEnterPacePassword": - if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_PIN) { + if (NumberModel.passwordType === NumberModel.PASSWORD_PIN) { setIdentifyWorkflowStateAndRequestInput(IdentifyController.WorkflowStates.Pin, "PIN") } - else if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_CAN) { + else if (NumberModel.passwordType === NumberModel.PASSWORD_CAN) { setIdentifyWorkflowStateAndRequestInput(IdentifyController.WorkflowStates.Can, "CAN") } - else if (NumberModel.establishPaceChannelType == PacePasswordId.PACE_PUK) { + else if (NumberModel.passwordType === NumberModel.PASSWORD_PUK) { AuthModel.cancelWorkflowOnPinBlocked() } break case "StateUnfortunateCardPosition": + //: INFO IOS The NFC signal is weak or unstable. The scan is stopped with this information in the iOS dialog. + ApplicationModel.stopNfcScanWithError(qsTr("Weak NFC signal") + SettingsModel.translationTrigger) firePush(cardPositionView) break case "StateDidAuthenticateEac1": controller.workflowProgressVisible = true - controller.workflowProgressValue = 1 setIdentifyWorkflowStateAndContinue(IdentifyController.WorkflowStates.Processing) break case "StateSendDIDAuthenticateResponseEAC1": fireReplace(identifyProgressView) AuthModel.continueWorkflow() break - case "StateDidAuthenticateEac2": - controller.workflowProgressValue = 2 - AuthModel.continueWorkflow() - break - case "StateTransmit": - controller.workflowProgressValue = 3 - AuthModel.continueWorkflow() - break case "StateCleanUpReaderManager": controller.connectedToCard = AuthModel.selectedReaderHasCard() && !AuthModel.isError; AuthModel.continueWorkflow() break; - case "StateCheckRefreshAddress": - controller.workflowProgressValue = 4 - AuthModel.continueWorkflow() - break case "StateWriteHistory": showRemoveCardFeedback() - controller.workflowProgressValue = 5 AuthModel.continueWorkflow() break case "StateShowSelfInfo": firePush(selfAuthenticationData) break case "StateSendWhitelistSurvey": - if (settingsModel.askForDeviceSurvey() && !AuthModel.error && d.readerPlugInType === ReaderPlugIn.NFC) { + if (SettingsModel.askForDeviceSurvey() && !AuthModel.error && d.readerPlugInType === ReaderPlugIn.NFC) { firePush(whiteListSurveyView) } else { AuthModel.continueWorkflow() @@ -173,7 +172,7 @@ Item { break case "FinalState": navBar.lockedAndHidden = true - if (AuthModel.error && !AuthModel.hasNextWorkflowPending) { + if (AuthModel.error && !AuthModel.hasNextWorkflowPending && !AuthModel.shouldSkipResultView()) { showRemoveCardFeedback() firePush(identifyResult) } else { @@ -182,7 +181,6 @@ Item { navBar.lockedAndHidden = false } controller.workflowProgressVisible = false - controller.workflowProgressValue = 0 break default: AuthModel.continueWorkflow() @@ -199,6 +197,7 @@ Item { if (AuthModel.isBasicReader) { enterPinView.state = pInput firePush(enterPinView) + ApplicationModel.nfcRunning = false } else { AuthModel.continueWorkflow() } diff --git a/resources/qml/Governikus/IdentifyView/+mobile/IdentifyView.qml b/resources/qml/Governikus/IdentifyView/+mobile/IdentifyView.qml index 058934a..aaa4099 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/IdentifyView.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/IdentifyView.qml @@ -1,15 +1,21 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 -import Governikus.MainView 1.0 -import Governikus.EnterPinView 1.0 +import Governikus.EnterPasswordView 1.0 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.ProgressView 1.0 import Governikus.ResultView 1.0 +import Governikus.SelfAuthenticationView 1.0 import Governikus.WhiteListClient 1.0 import Governikus.View 1.0 import Governikus.Workflow 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.AuthModel 1.0 import Governikus.Type.NumberModel 1.0 import Governikus.Type.ChangePinModel 1.0 @@ -18,11 +24,12 @@ import Governikus.Type.ChangePinModel 1.0 SectionPage { id: identifyEditChatView - leftTitleBarAction: TitleBarAction { + navigationAction: NavigationAction { state: ApplicationModel.currentWorkflow === "authentication" ? "cancel" : "" onClicked: AuthModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID IOS + title: qsTr("Identify") + SettingsModel.translationTrigger // Workaround for "cold start" case, IdentifyController onCompleted fires too early, TabBarView // for this View is not ready with loading the initial stack item. firePush of the ProgressView @@ -45,7 +52,7 @@ SectionPage } } - content: MainView { + content: SelfAuthenticationView { width: identifyEditChatView.width height: identifyEditChatView.height } @@ -74,7 +81,7 @@ SectionPage visible: false onDone: { - settingsModel.setDeviceSurveyPending(pUserAccepted) + SettingsModel.setDeviceSurveyPending(pUserAccepted) firePop() AuthModel.continueWorkflow() } @@ -86,7 +93,8 @@ SectionPage controller: identifyController workflowModel: AuthModel - workflowTitle: qsTr("Identify") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + workflowTitle: qsTr("Identify") + SettingsModel.translationTrigger waitingFor: switch (identifyController.workflowState) { case IdentifyController.WorkflowStates.Reader: @@ -101,13 +109,14 @@ SectionPage } } - EnterPinView { + EnterPasswordView { id: enterPinView - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: { firePop(); AuthModel.cancelWorkflow() } } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger } + navigationAction: NavigationAction { state: "cancel"; onClicked: { firePop(); AuthModel.cancelWorkflow() } } + //: LABEL ANDROID IOS + title: qsTr("Identify") + SettingsModel.translationTrigger visible: false - onPinEntered: { + onPasswordEntered: { firePop() AuthModel.continueWorkflow() } @@ -119,66 +128,75 @@ SectionPage ProgressView { id: identifyProgressView - leftTitleBarAction: TitleBarAction { state: AuthModel.isBasicReader || identifyController.workflowProgressVisible ? "cancel" : "hidden"; onClicked: AuthModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + navigationAction: NavigationAction { state: AuthModel.isBasicReader || identifyController.workflowProgressVisible ? "cancel" : "hidden"; onClicked: AuthModel.cancelWorkflow() } + //: LABEL ANDROID IOS + title: qsTr("Identify") + SettingsModel.translationTrigger visible: false + //: LABEL ANDROID IOS text: (AuthModel.error ? qsTr("Cancel authentication process") : + //: INFO ANDROID IOS Header of the progress status message during the authentication process. identifyController.workflowState === IdentifyController.WorkflowStates.Initial ? qsTr("Acquiring provider certificate") : - qsTr("Authentication in progress")) + settingsModel.translationTrigger + //: INFO ANDROID IOS Header of the progress status message during the authentication process. + qsTr("Authentication in progress")) + SettingsModel.translationTrigger subText: { - settingsModel.translationTrigger; + SettingsModel.translationTrigger; if (!visible) { return "" } if (AuthModel.isBasicReader) { + //: INFO ANDROID IOS Second line text if a basic card reader is used and background communication with the card/server is running. Is not actually visible since the basic reader password handling is done by EnterPasswordView. return qsTr("Please wait a moment...") } if (!!NumberModel.inputError) { return NumberModel.inputError } if (NumberModel.pinDeactivated) { + //: INFO ANDROID IOS The online authentication feature of the id card is disabled and needs to be actived the be authorities. return qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") } if (identifyController.workflowState === IdentifyController.WorkflowStates.Update || identifyController.workflowState === IdentifyController.WorkflowStates.Pin) { + //: INFO ANDROID IOS The card reader requests the user's attention. return qsTr("Please observe the display of your card reader.") } if (identifyController.workflowState === IdentifyController.WorkflowStates.Can) { + //: INFO ANDROID IOS The PIN was entered wrongfully two times, the third attempts requires additional CAN verification, hint where the CAN is found. return qsTr("You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card.") } + //: INFO ANDROID IOS Generic status message during the authentication process. return qsTr("Please wait a moment...") } subTextColor: !AuthModel.isBasicReader && (NumberModel.inputError || NumberModel.pinDeactivated || identifyController.workflowState === IdentifyController.WorkflowStates.Can) - ? "red" : Constants.secondary_text - progressValue: identifyController.workflowProgressValue - progressText: (progressValue === 0 ? "" : - progressValue === 1 ? qsTr("Service provider is being verified") : - progressValue === 2 ? qsTr("Card is being verified") : - progressValue === 3 ? qsTr("Reading data") : - progressValue === 4 ? qsTr("Sending data to service provider") : - progressValue === 5 ? qsTr("Preparing results") : - "") + settingsModel.translationTrigger + ? Style.color.warning_text : Style.color.secondary_text + progressValue: AuthModel.progressValue + progressText: AuthModel.progressMessage progressBarVisible: identifyController.workflowProgressVisible } ProgressView { id: checkConnectivityView - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: AuthModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + navigationAction: NavigationAction { state: "cancel"; onClicked: AuthModel.cancelWorkflow() } + //: LABEL ANDROID IOS + title: qsTr("Identify") + SettingsModel.translationTrigger visible: false - text: qsTr("No network connectivity") + settingsModel.translationTrigger - subText: qsTr("Please enable the network interface or cancel the workflow.") + settingsModel.translationTrigger - subTextColor: Constants.red + //: LABEL ANDROID IOS + text: qsTr("No network connectivity") + SettingsModel.translationTrigger + //: INFO ANDROID IOS No network connection, the user needs to active the network interface or abort the procedure. + subText: qsTr("Please enable the network interface or cancel the workflow.") + SettingsModel.translationTrigger + subTextColor: Style.color.warning_text } ResultView { id: changeToTransportPinView - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: fireReplace(enterPinView) } - headerTitleBarAction: TitleBarAction { text: qsTr("Change transport PIN") + settingsModel.translationTrigger; font.bold: true } + navigationAction: NavigationAction { state: "back"; onClicked: fireReplace(enterPinView) } + //: LABEL ANDROID IOS + title: qsTr("Change transport PIN") + SettingsModel.translationTrigger resultType: ResultView.Type.IsInfo - buttonText: qsTr("Change PIN") + settingsModel.translationTrigger - text: qsTr("You have to change your transport PIN into a personal PIN to use the online ID function. You are currently leaving the started process and are forwarded to the PIN management. Please restart the desired process after the PIN has been changed.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + buttonText: qsTr("Change PIN") + SettingsModel.translationTrigger + //: INFO ANDROID IOS The user clicked that the current PIN has 5 digits (transport PIN), it needs to be changed to an ordinary 6 digit PIN. The current process needs to be restarted *manually* by the user. + text: qsTr("You have to change your transport PIN into a personal PIN to use the online ID function. You are currently leaving the started process and are forwarded to the PIN management. Please restart the desired process after the PIN has been changed.") + SettingsModel.translationTrigger onClicked: { firePop() AuthModel.setSkipRedirect(true) @@ -190,10 +208,13 @@ SectionPage ResultView { id: cardPositionView - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID IOS + title: qsTr("Identify") + SettingsModel.translationTrigger resultType: ResultView.Type.IsInfo - buttonText: qsTr("Retry") + settingsModel.translationTrigger - text: qsTr("Weak NFC signal. Please reposition your card.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + buttonText: qsTr("Retry") + SettingsModel.translationTrigger + //: INFO ANDROID IOS The NFC signal is weak, by repositioning the card the signal might improve. + text: qsTr("Weak NFC signal. Please reposition your card.") + SettingsModel.translationTrigger onClicked: { firePop() AuthModel.continueWorkflow() @@ -203,7 +224,8 @@ SectionPage ResultView { id: identifyResult - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID IOS + title: qsTr("Identify") + SettingsModel.translationTrigger resultType: AuthModel.resultString ? ResultView.Type.IsError : ResultView.Type.IsSuccess showMailButton: AuthModel.errorIsMasked text: AuthModel.resultString diff --git a/resources/qml/Governikus/IdentifyView/+mobile/SelfAuthenticationData.qml b/resources/qml/Governikus/IdentifyView/+mobile/SelfAuthenticationData.qml index 5ff099a..ab786f7 100644 --- a/resources/qml/Governikus/IdentifyView/+mobile/SelfAuthenticationData.qml +++ b/resources/qml/Governikus/IdentifyView/+mobile/SelfAuthenticationData.qml @@ -1,14 +1,24 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 import Governikus.Type.AuthModel 1.0 +import Governikus.Type.SelfAuthModel 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: root - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: root.done() } - headerTitleBarAction: TitleBarAction { text: qsTr("Identify") + settingsModel.translationTrigger; font.bold: true } + + navigationAction: NavigationAction { state: "cancel"; onClicked: root.done() } + //: LABEL ANDROID IOS + title: qsTr("Identify") + SettingsModel.translationTrigger signal done() @@ -24,7 +34,7 @@ SectionPage { Item { id: message - height: Utils.dp(60) + height: 60 width: resultIcon.width + Constants.component_spacing + successText.width anchors.horizontalCenter: parent.horizontalCenter @@ -36,14 +46,14 @@ SectionPage { source: "qrc:///images/status_ok.svg" } - Text { + GText { id: successText anchors.left: resultIcon.right anchors.leftMargin: Constants.component_spacing anchors.verticalCenter: resultIcon.verticalCenter - text: qsTr("Successfull reading data") + settingsModel.translationTrigger - font.pixelSize: Constants.is_tablet ? Constants.header_font_size : Constants.normal_font_size - color: Constants.blue + //: INFO ANDROID IOS The self authentication was successfully completed. + text: qsTr("Read data successfully") + SettingsModel.translationTrigger + textStyle: Constants.is_tablet ? Style.text.header_accent : Style.text.normal_accent } } @@ -56,15 +66,19 @@ SectionPage { id: grid width: parent.width columns: Constants.is_tablet ? 3 : 1 - spacing: Utils.dp(15) + spacing: 15 verticalItemAlignment: Grid.AlignBottom Repeater { - model: selfAuthModel + model: SelfAuthModel LabeledText { + width: (pane.width - 2 * Constants.pane_padding - (grid.columns - 1) * grid.spacing) / grid.columns + + Accessible.onScrollDownAction: root.scrollPageDown() + Accessible.onScrollUpAction: root.scrollPageUp() + label: name text: value === "" ? "---" : value - width: (pane.width - 2 * Constants.pane_padding - (grid.columns - 1) * grid.spacing) / grid.columns } } } @@ -77,7 +91,8 @@ SectionPage { anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom anchors.bottomMargin: Constants.component_spacing - text: qsTr("OK") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("OK") + SettingsModel.translationTrigger onClicked: root.done() } } diff --git a/resources/qml/Governikus/InformationView/+desktop/DiagnosisView.qml b/resources/qml/Governikus/InformationView/+desktop/DiagnosisView.qml new file mode 100644 index 0000000..53985ff --- /dev/null +++ b/resources/qml/Governikus/InformationView/+desktop/DiagnosisView.qml @@ -0,0 +1,107 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.View 1.0 +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SelfDiagnosisModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +SectionPage { + id: sectionPage + + anchors.centerIn: parent + + Accessible.name: qsTr("Diagnosis view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the diagnosis view of the AusweisApp2.") + SettingsModel.translationTrigger + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Diagnosis") + SettingsModel.translationTrigger + } + + TabbedPane { + id: sectionContent + + anchors.fill: parent + anchors.margins: Constants.pane_padding + + sectionsModel: SelfDiagnosisModel.sectionsModel + contentDelegate: sectionDelegate + + footerItem: footerDelegate + } + + Component { + id: sectionDelegate + + Column { + height: implicitHeight + + spacing: Constants.pane_spacing + + Repeater { + readonly property string currentSectionName: sectionContent.currentItemModel.display + + model: SelfDiagnosisModel.getSectionContentModel(currentSectionName) + delegate: LabeledText { + width: parent.width + + activeFocusOnTab: true + + label: title + text: content + underlineLabel: title !== "" && content === "" + + onActiveFocusChanged: { + if (activeFocus) { + if (focusFrameMargins < 0) + sectionContent.scrollYPositionIntoView(y + height - focusFrameMargins) + else + sectionContent.scrollYPositionIntoView(y + height) + } + } + } + } + } + } + + Component { + id: footerDelegate + + Item { + height: saveToFile.height + + GButton { + id: saveToFile + + Accessible.name: saveToFile.text + Accessible.description: qsTr("Save diagnosis to textfile") + SettingsModel.translationTrigger + + icon.source: "qrc:///images/icon_save.svg" + //: LABEL DESKTOP_QML + text: qsTr("Save to file") + SettingsModel.translationTrigger + onClicked: { + var filenameSuggestion = "%1.%2.%3.txt".arg(Qt.application.name).arg(qsTr("Diagnosis")).arg(SelfDiagnosisModel.getCreationTimeString()) + appWindow.openSaveFileDialog(SelfDiagnosisModel.saveToFile, filenameSuggestion, "txt") + } + } + } + } + + onVisibleChanged: { + if (visible) { + SelfDiagnosisModel.startController() + sectionContent.currentIndex = 0 + } + else { + SelfDiagnosisModel.stopController() + } + } +} diff --git a/resources/qml/Governikus/InformationView/+desktop/InformationView.qml b/resources/qml/Governikus/InformationView/+desktop/InformationView.qml new file mode 100644 index 0000000..08b1206 --- /dev/null +++ b/resources/qml/Governikus/InformationView/+desktop/InformationView.qml @@ -0,0 +1,370 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.FeedbackView 1.0 +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +SectionPage { + id: sectionPage + + enum SubViews { + None, + Diagnosis, + ApplicationLog, + VersionInformation + } + + readonly property int cellHeight: (height - 2 * gridLayout.anchors.margins) / 4 + readonly property int informationViewIconWidth: width / 6 + readonly property int separatorHeight: Math.max(1, ApplicationModel.scaleFactor * 4) + property int activeSubView + + Accessible.name: qsTr("Help section") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the help section of the AusweisApp2.") + SettingsModel.translationTrigger + Keys.onEscapePressed: { + if (activeSubView === InformationView.SubViews.None) { + event.accepted = false + return + } + + activeSubView = InformationView.SubViews.None + } + + isAbstract: activeSubView !== InformationView.SubViews.None + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Help") + SettingsModel.translationTrigger + onClicked: activeSubView = InformationView.SubViews.None + } + onVisibleChanged: activeSubView = InformationView.SubViews.None + + GridLayout { + id: gridLayout + + readonly property int iconAlignment: Qt.AlignTop | Qt.AlignHCenter + readonly property int longestText: Math.max(textQuestionsFeedbackRating.width, + textSetupManual.width, + textDiagnosisApplicationLog.width, + textVersionAndLicense.width) + + visible: activeSubView === InformationView.SubViews.None + anchors.fill: sectionPage + anchors.margins: Constants.pane_padding + anchors.topMargin: anchors.margins + (sectionPage.cellHeight - iconDiagnosis.implicitHeight) / 2 + + rowSpacing: 0 + columnSpacing: 0 + columns: 4 + rows: 4 + onVisibleChanged: if (visible) sectionPage.setActive() + + Column { + Layout.fillWidth: true + Layout.fillHeight: true + + topPadding: (implicitHeight - textQuestionsFeedbackRating.height - separatorHeight) / 2 + spacing: Constants.component_spacing + + GText { + id: textQuestionsFeedbackRating + + Accessible.name: text + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Questions, feedback, and rating") + SettingsModel.translationTrigger + textStyle: Style.text.header + + FocusFrame {} + } + Rectangle { + height: sectionPage.separatorHeight + width: gridLayout.longestText * 1.2 + + color: Constants.white + opacity: 0.5 + } + } + InformationViewIcon{ + id: iconQuestions + + width: sectionPage.informationViewIconWidth + + Layout.alignment: gridLayout.iconAlignment + Layout.minimumHeight: sectionPage.cellHeight + + activeFocusOnTab: true + + source: "qrc:/images/desktop/info_questions.svg" + //: LABEL DESKTOP_QML + text: qsTr("Questions") + SettingsModel.translationTrigger + onClicked: Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/frequently-asked-questions/")) + SettingsModel.translationTrigger + } + InformationViewIcon { + id: iconReportError + + width: sectionPage.informationViewIconWidth + + Layout.alignment: gridLayout.iconAlignment + Layout.minimumHeight: sectionPage.cellHeight + + activeFocusOnTab: true + + source: "qrc:/images/desktop/info_report_error.svg" + //: LABEL DESKTOP_QML + text: qsTr("Report error") + SettingsModel.translationTrigger + onClicked: Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/report-an-error/")) + SettingsModel.translationTrigger + } + InformationViewIcon { + id: iconRateApplication + + width: sectionPage.informationViewIconWidth + + Layout.alignment: gridLayout.iconAlignment + Layout.minimumHeight: sectionPage.cellHeight + + activeFocusOnTab: true + + source: "qrc:/images/desktop/info_rate_application.svg" + //: LABEL DESKTOP_QML + text: qsTr("Rate application") + SettingsModel.translationTrigger + onClicked: Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/evaluate-us/")) + SettingsModel.translationTrigger + } + + Column { + Layout.fillHeight: true + Layout.fillWidth: true + + topPadding: (implicitHeight - textSetupManual.height - separatorHeight) / 2 + spacing: Constants.component_spacing + + GText { + id: textSetupManual + + Accessible.name: text + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Setup and manual") + SettingsModel.translationTrigger + textStyle: Style.text.header + + FocusFrame {} + } + Rectangle { + height: sectionPage.separatorHeight + width: gridLayout.longestText * 1.2 + + color: Constants.white + opacity: 0.5 + } + } + Item { + id: placeHolder1 + + width: 1 + height: 1 + } + InformationViewIcon { + id: iconSetup + + width: sectionPage.informationViewIconWidth + + Layout.alignment: gridLayout.iconAlignment + Layout.minimumHeight: sectionPage.cellHeight + + activeFocusOnTab: true + + source: "qrc:/images/desktop/info_setup.svg" + //: LABEL DESKTOP_QML + text: qsTr("Setup") + SettingsModel.translationTrigger + onClicked: sectionPage.nextView(SectionPage.Views.SetupAssistant) + } + InformationViewIcon { + id: iconManual + + width: sectionPage.informationViewIconWidth + + Layout.minimumHeight: sectionPage.cellHeight + Layout.alignment: gridLayout.iconAlignment + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Online help") + SettingsModel.translationTrigger + source: "qrc:/images/desktop/info_manual.svg" + onClicked: ApplicationModel.openOnlineHelp("index") + } + + Column { + Layout.fillHeight: true + Layout.fillWidth: true + + topPadding: (implicitHeight - textDiagnosisApplicationLog.height - separatorHeight) / 2 + spacing: Constants.component_spacing + + GText { + id: textDiagnosisApplicationLog + + Accessible.name: text + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Diagnosis and application log") + SettingsModel.translationTrigger + textStyle: Style.text.header + + FocusFrame {} + } + Rectangle { + height: sectionPage.separatorHeight + width: gridLayout.longestText * 1.2 + + color: Constants.white + opacity: 0.5 + } + } + Item { + id: placeHolder2 + + width: 1 + height: 1 + } + InformationViewIcon { + id: iconDiagnosis + + width: sectionPage.informationViewIconWidth + + Layout.alignment: gridLayout.iconAlignment + Layout.minimumHeight: sectionPage.cellHeight + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Diagnosis") + SettingsModel.translationTrigger + source: "qrc:/images/desktop/info_diagnosis.svg" + onClicked: activeSubView = InformationView.SubViews.Diagnosis + } + InformationViewIcon { + id: iconApplicationLog + + width: sectionPage.informationViewIconWidth + + Layout.alignment: gridLayout.iconAlignment + Layout.minimumHeight: sectionPage.cellHeight + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Application log") + SettingsModel.translationTrigger + source: "qrc:/images/desktop/info_application_log.svg" + onClicked: activeSubView = InformationView.SubViews.ApplicationLog + } + + Column { + Layout.fillHeight: true + Layout.fillWidth: true + + topPadding: (implicitHeight - textVersionAndLicense.height - separatorHeight) / 2 + spacing: Constants.component_spacing + + GText { + id: textVersionAndLicense + + Accessible.name: text + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Version and license information") + SettingsModel.translationTrigger + textStyle: Style.text.header + + FocusFrame {} + } + Rectangle { + height: sectionPage.separatorHeight + width: gridLayout.longestText * 1.2 + + color: Constants.white + opacity: 0.5 + } + } + Item { + id: placeHolder3 + + width: 1 + height: 1 + } + InformationViewIcon { + id: iconVersionInformation + + width: sectionPage.informationViewIconWidth + + Layout.minimumHeight: sectionPage.cellHeight + Layout.alignment: gridLayout.iconAlignment + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Version information") + SettingsModel.translationTrigger + source: "qrc:/images/desktop/info_version.svg" + onClicked: activeSubView = InformationView.SubViews.VersionInformation + } + InformationViewIcon { + id: iconLicense + + width: sectionPage.informationViewIconWidth + + Layout.minimumHeight: sectionPage.cellHeight + Layout.alignment: gridLayout.iconAlignment + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Software licenses") + SettingsModel.translationTrigger + source: "qrc:/images/desktop/info_license.svg" + onClicked: Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/windows-and-mac/")) + } + } + + Component { + id: diagnosisView + + DiagnosisView {} + } + + Component { + id: versionInformation + + VersionInformation {} + } + + Component { + id: logFileView + + LogView {} + } + + Loader { + readonly property bool sectionPageTypeMarker: true + property var titleBarAction: item ? item.titleBarAction : undefined + + visible: item // Otherwise onVisibleChildrenChanged and onVisibleChanged won't be triggered + anchors.fill: parent + + sourceComponent: switch(sectionPage.activeSubView) { + case InformationView.SubViews.Diagnosis: return diagnosisView + case InformationView.SubViews.VersionInformation: return versionInformation + case InformationView.SubViews.ApplicationLog: return logFileView + } + } +} diff --git a/resources/qml/Governikus/InformationView/+desktop/InformationViewIcon.qml b/resources/qml/Governikus/InformationView/+desktop/InformationViewIcon.qml new file mode 100644 index 0000000..efdbeb2 --- /dev/null +++ b/resources/qml/Governikus/InformationView/+desktop/InformationViewIcon.qml @@ -0,0 +1,60 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +FocusScope { + id: root + + property alias source: img.source + property alias text: label.text + property int iconSize: ApplicationModel.scaleFactor * 120 + + signal clicked() + + implicitHeight: label.height + label.anchors.topMargin + img.height + implicitWidth: Math.max(label.width, img.width) + + Accessible.role: Accessible.Button + Accessible.name: text + + Keys.onSpacePressed: root.clicked() + + MouseArea { + anchors.fill: focusFrame + cursorShape: Qt.PointingHandCursor + onPressed: parent.focus = true + onClicked: parent.clicked() + } + + FocusFrame { + id: focusFrame + framee: img + marginFactor: 2.0 + } + + FramedImage { + id: img + width: iconSize + height: iconSize + anchors.top: parent.top + border.color: Constants.white + anchors.horizontalCenter: parent.horizontalCenter + } + + GText { + id: label + width: parent.width + anchors.top: img.bottom + anchors.topMargin: Constants.component_spacing + horizontalAlignment: Text.AlignHCenter + font.bold: true + } +} diff --git a/resources/qml/Governikus/InformationView/+desktop/VersionInformation.qml b/resources/qml/Governikus/InformationView/+desktop/VersionInformation.qml new file mode 100644 index 0000000..0e026f3 --- /dev/null +++ b/resources/qml/Governikus/InformationView/+desktop/VersionInformation.qml @@ -0,0 +1,59 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + + +SectionPage { + id: root + + Accessible.name: qsTr("Version information") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the version information section of the AusweisApp2.") + SettingsModel.translationTrigger + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Version information") + SettingsModel.translationTrigger + } + + ScrollablePane { + id: pane + + anchors.fill: root + anchors.margins: Constants.pane_padding + + //: LABEL DESKTOP_QML + title: qsTr("Version information") + SettingsModel.translationTrigger + + activeFocusOnTab: true + + Column { + id: column + + spacing: Constants.component_spacing + + Repeater { + id: repeater + + model: versionInformationModel + delegate: LabeledText { + id: delegate + + width: root.width + + Accessible.name: model.label + ": " + model.text + activeFocusOnTab: true + + label: model.label + text: model.text + } + } + } + } +} diff --git a/resources/qml/Governikus/InformationView/Information.qml b/resources/qml/Governikus/InformationView/+mobile/InformationView.qml similarity index 67% rename from resources/qml/Governikus/InformationView/Information.qml rename to resources/qml/Governikus/InformationView/+mobile/InformationView.qml index 162e7a3..443ff1f 100644 --- a/resources/qml/Governikus/InformationView/Information.qml +++ b/resources/qml/Governikus/InformationView/+mobile/InformationView.qml @@ -1,44 +1,51 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: root - headerTitleBarAction: TitleBarAction { text: qsTr("Information") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID IOS + title: qsTr("Information") + SettingsModel.translationTrigger - Component { - id: lineSeparator - Rectangle { - height: 1 - color: Constants.grey - } - } Component { id: subMenu Item { height: column.height + + Accessible.role: Accessible.Button + Accessible.name: titleText + " " + descriptionText + Column { id: column anchors.left: parent.left anchors.right: parent.right spacing: Constants.component_spacing - Text { + GText { width: parent.width - font.pixelSize: Utils.dp(18) - color: Constants.blue - wrapMode: Text.WordWrap + + Accessible.ignored: true + text: titleText + textStyle: Style.text.normal_accent } - Text { - color: Constants.secondary_text + GText { width: parent.width - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap + + Accessible.ignored: true + text: descriptionText + textStyle: Style.text.normal } } MouseArea { @@ -65,24 +72,31 @@ SectionPage { Pane { Loader { - readonly property string titleText: qsTr("Version information") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("Here you can see detailed information about AusweisApp2.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string titleText: qsTr("Version information") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string descriptionText: qsTr("Here you can see detailed information about AusweisApp2.") + SettingsModel.translationTrigger function onClickFunction() { firePush(versionInformationPage) } width: parent.width sourceComponent: subMenu } - Loader { width: parent.width; sourceComponent: lineSeparator } + GSeparator { width: parent.width } Loader { - readonly property string titleText: qsTr("Software license") + settingsModel.translationTrigger - readonly property string descriptionText: qsTr("Read the software license text on the application homepage.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string titleText: qsTr("Software license") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + readonly property string descriptionText: qsTr("Read the software license text on the application homepage.") + SettingsModel.translationTrigger function onClickFunction() { if (Qt.platform.os === "android") { + //: LABEL ANDROID IOS Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/android/")) } else if (Qt.platform.os === "ios") { + //: LABEL ANDROID IOS Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/ios/")) } else { + //: LABEL ANDROID IOS Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/windows-and-mac/")) } } diff --git a/resources/qml/Governikus/InformationView/VersionInformation.qml b/resources/qml/Governikus/InformationView/+mobile/VersionInformation.qml similarity index 59% rename from resources/qml/Governikus/InformationView/VersionInformation.qml rename to resources/qml/Governikus/InformationView/+mobile/VersionInformation.qml index 97d3157..3384da4 100644 --- a/resources/qml/Governikus/InformationView/VersionInformation.qml +++ b/resources/qml/Governikus/InformationView/+mobile/VersionInformation.qml @@ -1,15 +1,21 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 SectionPage { id: root - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Version Information") + settingsModel.translationTrigger; font.bold: true } + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + //: LABEL ANDROID IOS + title: qsTr("Version Information") + SettingsModel.translationTrigger content: Item { @@ -29,9 +35,12 @@ SectionPage LabeledText { id: delegate + + anchors.left: parent.left + anchors.right: parent.right + label: model.label text: model.text - width: pane.width } } } diff --git a/resources/qml/Governikus/InformationView/qmldir b/resources/qml/Governikus/InformationView/qmldir index 0b05560..765a84e 100644 --- a/resources/qml/Governikus/InformationView/qmldir +++ b/resources/qml/Governikus/InformationView/qmldir @@ -1,4 +1,8 @@ module InformationView -Information 1.0 Information.qml +internal DiagnosisView DiagnosisView.qml +internal InformationViewIcon InformationViewIcon.qml + +InformationView 1.0 InformationView.qml VersionInformation 1.0 VersionInformation.qml + diff --git a/resources/qml/Governikus/MainView/+desktop/MainView.qml b/resources/qml/Governikus/MainView/+desktop/MainView.qml index 51fdf2d..81a7119 100644 --- a/resources/qml/Governikus/MainView/+desktop/MainView.qml +++ b/resources/qml/Governikus/MainView/+desktop/MainView.qml @@ -1,14 +1,21 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.SelfAuthModel 1.0 +import Governikus.Type.ChangePinModel 1.0 SectionPage { id: sectionPage - KeyNavigation.tab: tileIdentify - Accessible.role: Accessible.Grouping Accessible.name: qsTr("Main view") Accessible.description: qsTr("This is the main view of the AusweisApp2.") @@ -31,19 +38,20 @@ SectionPage { height: parent.height width: view.horizontalItemSpace - title: qsTr("Identify") + //: LABEL DESKTOP_QML + title: qsTr("Self-authentication") + SettingsModel.translationTrigger image: "qrc:/images/desktop/main_identify.svg" - onClicked: selfAuthModel.startWorkflow() + onClicked: sectionPage.nextView(SectionPage.Views.SelfAuthentication) - KeyNavigation.tab: tileProvider + activeFocusOnTab: true } Rectangle { height: view.verticalItemSpace * 2/3 width: view.separatorLineWidth anchors.verticalCenter: parent.verticalCenter - color: Constants.grey_light + color: Style.color.border } Tile { @@ -52,19 +60,20 @@ SectionPage { height: view.verticalItemSpace width: view.horizontalItemSpace - title: qsTr("Provider") + //: LABEL DESKTOP_QML + title: qsTr("Provider") + SettingsModel.translationTrigger image: "qrc:/images/desktop/main_provider.svg" onClicked: sectionPage.nextView(SectionPage.Views.Provider) - KeyNavigation.tab: tileHistory + activeFocusOnTab: true } Rectangle { height: view.verticalItemSpace * 2/3 width: view.separatorLineWidth anchors.verticalCenter: parent.verticalCenter - color: Constants.grey_light + color: Style.color.border } Tile { @@ -73,10 +82,13 @@ SectionPage { height: view.verticalItemSpace width: view.horizontalItemSpace - title: qsTr("History") + //: LABEL DESKTOP_QML + title: qsTr("History") + SettingsModel.translationTrigger image: "qrc:/images/desktop/main_history.svg" - KeyNavigation.tab: tileSettings + onClicked: sectionPage.nextView(SectionPage.Views.History) + + activeFocusOnTab: true } } @@ -85,7 +97,7 @@ SectionPage { width: parent.width - view.horizontalItemSpace / 3 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - color: Constants.grey_light + color: Style.color.border } Row { @@ -99,17 +111,20 @@ SectionPage { height: view.verticalItemSpace width: view.horizontalItemSpace - title: qsTr("Settings") + //: LABEL DESKTOP_QML + title: qsTr("Settings") + SettingsModel.translationTrigger image: "qrc:/images/desktop/settings_icon.svg" - KeyNavigation.tab: tilePin + onClicked: sectionPage.nextView(SectionPage.Views.Settings) + + activeFocusOnTab: true } Rectangle { height: view.verticalItemSpace * 2/3 width: view.separatorLineWidth anchors.verticalCenter: parent.verticalCenter - color: Constants.grey_light + color: Style.color.border } Tile { @@ -118,17 +133,20 @@ SectionPage { height: view.verticalItemSpace width: view.horizontalItemSpace - title: qsTr("PIN management") + //: LABEL DESKTOP_QML + title: qsTr("PIN management") + SettingsModel.translationTrigger image: "qrc:/images/desktop/main_pin.svg" - KeyNavigation.tab: tileHelp + onClicked: ChangePinModel.startWorkflow() + + activeFocusOnTab: true } Rectangle { height: view.verticalItemSpace * 2/3 width: view.separatorLineWidth anchors.verticalCenter: parent.verticalCenter - color: Constants.grey_light + color: Style.color.border } Tile { @@ -137,10 +155,13 @@ SectionPage { height: view.verticalItemSpace width: view.horizontalItemSpace - title: qsTr("Help") + //: LABEL DESKTOP_QML + title: qsTr("Help") + SettingsModel.translationTrigger image: "qrc:/images/desktop/help_icon.svg" - KeyNavigation.tab: sectionPage.navSuccessor + activeFocusOnTab: true + + onClicked: sectionPage.nextView(SectionPage.Views.Information) } } } diff --git a/resources/qml/Governikus/MainView/+desktop/Tile.qml b/resources/qml/Governikus/MainView/+desktop/Tile.qml index e02bd4e..0a6f44d 100644 --- a/resources/qml/Governikus/MainView/+desktop/Tile.qml +++ b/resources/qml/Governikus/MainView/+desktop/Tile.qml @@ -1,7 +1,13 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 FocusScope { id: tile @@ -20,6 +26,7 @@ FocusScope { anchors.fill: parent onPressed: tile.focus = true onClicked: tile.clicked() + cursorShape: Qt.PointingHandCursor } FocusFrame { @@ -28,20 +35,23 @@ FocusScope { Column { anchors.centerIn: parent - spacing: text.height / 2 + + spacing: Constants.component_spacing Image { id: image - sourceSize.height: text.height * 4 + + sourceSize.height: ApplicationModel.scaleFactor * 200 anchors.horizontalCenter: parent.horizontalCenter } GText { id: text + anchors.horizontalCenter: parent.horizontalCenter - color: Constants.white - font.pixelSize: Constants.header_font_size - font.weight: Font.Bold + + textStyle: Style.text.title + font.bold: true } } } diff --git a/resources/qml/Governikus/MoreView/+mobile/MoreView.qml b/resources/qml/Governikus/MoreView/+mobile/MoreView.qml new file mode 100644 index 0000000..3754a1b --- /dev/null +++ b/resources/qml/Governikus/MoreView/+mobile/MoreView.qml @@ -0,0 +1,204 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.2 + +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.FeedbackView 1.0 +import Governikus.InformationView 1.0 +import Governikus.HistoryView 1.0 +import Governikus.DeveloperView 1.0 +import Governikus.TutorialView 1.0 +import Governikus.View 1.0 +import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.LogModel 1.0 + + +SectionPage { + id: moreView + + //: LABEL ANDROID IOS DESKTOP_QML + title: qsTr("More") + SettingsModel.translationTrigger + + content: ColumnLayout { + id: menu + + width: moreView.width + + spacing: 0 + + GSeparator { + Layout.topMargin: Constants.component_spacing + Layout.fillWidth: true + } + + MoreViewMenuItem { + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("Version information") + SettingsModel.translationTrigger + icon: "qrc:///images/iOS/more/icon_mehr_npa.svg" + showRightArrow: true + onClicked: firePush(versionInformationPage) + } + + MoreViewMenuItem { + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("Tutorial") + SettingsModel.translationTrigger + icon: "qrc:///images/iOS/more/icon_mehr_tutorial.svg" + showRightArrow: true + onClicked: firePush(tutorialView) + } + + MoreViewMenuItem { + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("FAQ") + SettingsModel.translationTrigger + icon: "qrc:///images/iOS/more/icon_mehr_info.svg" + //: LABEL ANDROID IOS DESKTOP_QML + onClicked: Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/frequently-asked-questions/")) + } + + MoreViewMenuItem { + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("Support") + SettingsModel.translationTrigger + icon: "qrc:///images/iOS/more/icon_mehr_fragen.svg" + //: LABEL ANDROID IOS DESKTOP_QML + onClicked: Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/support/")) + } + + MoreViewMenuItem { + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("Rate app") + SettingsModel.translationTrigger; + icon: "qrc:///images/iOS/more/icon_mehr_favorit.svg"; + onClicked: { + let ratingUrl = Qt.platform.os === "android" ? "market://details?id=com.governikus.ausweisapp2" + : "itms-apps:itunes.apple.com/%1/app/ausweisapp2/id948660805?mt=8&action=write-review".arg(SettingsModel.language) + Qt.openUrlExternally(ratingUrl) + } + } + + MoreViewMenuItem { + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("Report error") + SettingsModel.translationTrigger + icon: "qrc:///images/iOS/more/icon_mehr_report.svg" + onClicked: LogModel.mailLog() + } + + MoreViewMenuItem { + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("Software license") + SettingsModel.translationTrigger + icon: "qrc:///images/iOS/more/icon_mehr_license.svg" + onClicked: { + if (Qt.platform.os === "android") { + //: LABEL ANDROID IOS DESKTOP_QML + Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/android/")) + } + else if (Qt.platform.os === "ios") { + //: LABEL ANDROID IOS DESKTOP_QML + Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/ios/")) + } + else { + //: LABEL ANDROID IOS DESKTOP_QML + Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/windows-and-mac/")) + } + } + } + + MoreViewMenuItem { + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("History") + SettingsModel.translationTrigger + icon: "qrc:///images/iOS/more/icon_mehr_verlauf.svg" + showRightArrow: true + onClicked: { + firePush(historyView) + } + } + + MoreViewMenuItem { + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("Show log") + SettingsModel.translationTrigger + icon: "qrc:///images/iOS/more/icon_mehr_log.svg" + showRightArrow: true + showSeparator: plugin.developerBuild + onClicked: firePush(logView) + } + + MoreViewMenuItem { + visible: plugin.developerBuild + //: LABEL ANDROID IOS DESKTOP_QML + text: qsTr("Developer options") + SettingsModel.translationTrigger + icon: "qrc:///images/zahnraeder.svg" + showRightArrow: true + showSeparator: false + onClicked: firePush(developerView) + } + + GSeparator { + Layout.bottomMargin: 2 * Constants.component_spacing + languageButtons.childrenRect.height + Layout.fillWidth: true + } + } + + Item { + id: languageButtons + + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + LocationButton { + id: lang_de + + anchors.margins: Constants.component_spacing + anchors.bottom: parent.bottom + anchors.right: lang_en.left + + Accessible.name: qsTr("Set language to german") + SettingsModel.translationTrigger + + language: "de" + name: "DE" + image: "qrc:///images/location_flag_de.svg" + } + + LocationButton { + id: lang_en + + anchors.margins: Constants.component_spacing + anchors.bottom: parent.bottom + anchors.right: parent.right + + Accessible.name: qsTr("Set language to english") + SettingsModel.translationTrigger + + language: "en" + name: "EN" + image: "qrc:///images/location_flag_en.svg" + } + } + + Component { + id: versionInformationPage + VersionInformation {} + } + + Component { + id: historyView + HistoryView {} + } + + Component { + id: developerView + DeveloperView {} + } + + Component { + id: logView + LogView {} + } + + Component { + id: tutorialView + TutorialView {} + } +} diff --git a/resources/qml/Governikus/MoreView/+mobile/MoreViewMenuItem.qml b/resources/qml/Governikus/MoreView/+mobile/MoreViewMenuItem.qml new file mode 100644 index 0000000..ede3576 --- /dev/null +++ b/resources/qml/Governikus/MoreView/+mobile/MoreViewMenuItem.qml @@ -0,0 +1,16 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtGraphicalEffects 1.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + +ListItem { + implicitHeight: 44 + implicitWidth: parent.width + + showRightArrow: false +} diff --git a/resources/qml/Governikus/MoreView/MoreView.qml b/resources/qml/Governikus/MoreView/MoreView.qml deleted file mode 100644 index aebacbb..0000000 --- a/resources/qml/Governikus/MoreView/MoreView.qml +++ /dev/null @@ -1,174 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Layouts 1.2 - -import Governikus.Global 1.0 -import Governikus.TitleBar 1.0 -import Governikus.FeedbackView 1.0 -import Governikus.InformationView 1.0 -import Governikus.RemoteServiceView 1.0 -import Governikus.DeveloperView 1.0 -import Governikus.TutorialView 1.0 -import Governikus.View 1.0 -import Governikus.Type.RemoteServiceModel 1.0 - -SectionPage { - headerTitleBarAction: TitleBarAction { text: qsTr("More") + settingsModel.translationTrigger; font.bold: true } - - Rectangle { - anchors.fill: menu - color: "white" - } - - Column { - id: menu - width: parent.width - anchors.top: parent.top - anchors.topMargin: Constants.component_spacing - - MoreViewMenuItem { - text: qsTr("Version information") + settingsModel.translationTrigger - imageSource: "qrc:///images/npa.svg" - showRightArrow: true - onClicked: firePush(versionInformationPage) - } - - MoreViewMenuItem { - text: qsTr("Tutorial") + settingsModel.translationTrigger - imageSource: "qrc:///images/iOS/more/icon_mehr_tutorial.svg" - showRightArrow: true - onClicked: firePush(tutorialView) - } - - MoreViewMenuItem { - text: qsTr("FAQ") + settingsModel.translationTrigger - imageSource: "qrc:///images/iOS/more/icon_mehr_info.svg" - onClicked: Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/frequently-asked-questions/")) - } - - MoreViewMenuItem { - text: qsTr("Support") + settingsModel.translationTrigger - imageSource: "qrc:///images/iOS/more/icon_mehr_fragen.svg" - onClicked: Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/support/")) - } - - MoreViewMenuItem { - text: qsTr("Rate app") + settingsModel.translationTrigger; - imageSource: "qrc:///images/iOS/more/icon_mehr_favorit.svg"; - // Use Qt.platform.os here instead of platformstyle because rating market URLs on iOS doesn't work and vice versa - onClicked: { - if (Qt.platform.os === "android") { - Qt.openUrlExternally("market://details?id=com.governikus.ausweisapp2") - } - else if (Qt.platform.os === "ios") { - Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/qa/evaluate-us/")) - } - } - } - - MoreViewMenuItem { - text: qsTr("Software license") + settingsModel.translationTrigger - imageSource: "qrc:///images/iOS/more/icon_mehr_license.svg" - onClicked: { - if (Qt.platform.os === "android") { - Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/android/")) - } - else if (Qt.platform.os === "ios") { - Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/ios/")) - } - else { - Qt.openUrlExternally(qsTr("https://www.ausweisapp.bund.de/en/download/windows-and-mac/")) - } - } - } - - MoreViewMenuItem { - imageSource: "qrc:///images/iOS/more/icon_mehr_remotereader" - text: qsTr("Configure remote service") + settingsModel.translationTrigger - showRightArrow: true - onClicked: { - firePush(remoteServiceView) - } - } - - MoreViewMenuItem { - text: qsTr("Show log") + settingsModel.translationTrigger - imageSource: "qrc:///images/iOS/more/icon_mehr_log.svg" - showRightArrow: true - onClicked: firePush(logView) - } - - MoreViewMenuItem { - visible: plugin.developerBuild - text: qsTr("Developer options") + settingsModel.translationTrigger - imageSource: "qrc:///images/zahnraeder.svg" - showRightArrow: true - onClicked: firePush(developerView) - } - } - - Item { - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - LocationButton { - id: lang_de - - language: "de" - name: "DE" - image: "qrc:///images/location_flag_de.svg" - - anchors.margins: Constants.component_spacing - anchors.bottom: parent.bottom - anchors.right: lang_en.left - } - - LocationButton { - id: lang_en - - language: "en" - name: "EN" - image: "qrc:///images/location_flag_en.svg" - - anchors.margins: Constants.component_spacing - anchors.bottom: parent.bottom - anchors.right: parent.right - } - } - - Rectangle { - anchors.top: menu.top - height: 1; width: parent.width - color: Constants.grey - } - Rectangle { - anchors.bottom: menu.bottom - height: 1; width: parent.width - color: Constants.grey - } - - VersionInformation { - id: versionInformationPage - visible: false - } - - RemoteServiceView { - id: remoteServiceView - visible: false - } - - DeveloperView { - id: developerView - visible: false - } - - Log { - id: logView - visible: false - } - - TutorialView { - id: tutorialView - visible: false - } -} diff --git a/resources/qml/Governikus/MoreView/MoreViewMenuItem.qml b/resources/qml/Governikus/MoreView/MoreViewMenuItem.qml deleted file mode 100644 index 6238d51..0000000 --- a/resources/qml/Governikus/MoreView/MoreViewMenuItem.qml +++ /dev/null @@ -1,59 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 - - -Item { - property alias imageSource: imageItem.source - property alias text: textItem.text - property bool showRightArrow: false - signal clicked - height: Utils.dp(40) - width: parent.width - - Image { - id: imageItem - fillMode: Image.PreserveAspectFit - height: parent.height * 0.6 - width: height - anchors.left: parent.left - anchors.leftMargin: Utils.dp(15) - anchors.verticalCenter: parent.verticalCenter - } - - Text { - id: textItem - color: Constants.secondary_text - height: parent.height - verticalAlignment: Text.AlignVCenter - anchors.left: imageItem.right - anchors.leftMargin: Utils.dp(10) - anchors.verticalCenter: parent.verticalCenter - font.pixelSize: Utils.dp(16) - } - - Text { - anchors.right: parent.right - anchors.rightMargin: Utils.dp(5) - anchors.verticalCenter: parent.verticalCenter - - text: ">" - color: Constants.grey - font.pixelSize: Constants.normal_font_size - visible: parent.showRightArrow - } - - MouseArea { - anchors.fill: parent - onClicked: parent.clicked() - } - - Rectangle { - anchors.bottom: parent.bottom - anchors.left: textItem.left - anchors.right: parent.right - height: 1 - color: Constants.grey - } -} - diff --git a/resources/qml/Governikus/Navigation/+android/Navigation.qml b/resources/qml/Governikus/Navigation/+android/Navigation.qml index 6e0ebcb..79861ed 100644 --- a/resources/qml/Governikus/Navigation/+android/Navigation.qml +++ b/resources/qml/Governikus/Navigation/+android/Navigation.qml @@ -1,21 +1,30 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + Item { property int leftOverlayMargin: 0 id: navigation - width: !Constants.is_tablet || lockedAndHidden ? 0 : Constants.menubar_width + width: !Constants.is_tablet || lockedAndHidden ? 0 : Style.dimens.menubar_width enabled: !lockedAndHidden property bool lockedAndHidden: true property bool isOpen: drawer.position > 0 property int currentIndex + signal reselectedState + Component.onCompleted: { - state = settingsModel.showTutorialOnStart ? "tutorial" : "identify" - currentIndex = settingsModel.showTutorialOnStart ? 5 : 0 + state = SettingsModel.showSetupAssistantOnStart ? "tutorial" : "identify" + currentIndex = SettingsModel.showSetupAssistantOnStart ? 5 : 0 } onLockedAndHiddenChanged: { @@ -44,35 +53,36 @@ Item { anchors.left: parent.left anchors.bottom: parent.bottom width: Math.max(parent.width, navigation.leftOverlayMargin); - color: Constants.background_color + color: Style.color.background clip: true NavigationView { navigationController: navigation } - Rectangle { + GSeparator { id: iconBarBorder anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom - width: Utils.dp(2) - color: Constants.grey_border + orientation: Qt.Vertical + color: Style.color.border_dark } } Drawer { id: drawer - x: Constants.is_tablet ? Constants.menubar_width : 0 - y: Constants.titlebar_height + x: Constants.is_tablet ? Style.dimens.menubar_width : 0 + y: Style.dimens.titlebar_height + plugin.safeAreaMargins.top + height: navigation.height - width: Utils.dp(250) + width: 250 opacity: Constants.is_tablet ? 0 : 1 - dragMargin: lockedAndHidden ? 0 : Utils.dp(Qt.styleHints.startDragDistance) + dragMargin: lockedAndHidden ? 0 : Qt.styleHints.startDragDistance onPositionChanged: { if (Constants.is_tablet && position > 0) { - navigation.leftOverlayMargin = Constants.menubar_width + (width - Constants.menubar_width) * position + navigation.leftOverlayMargin = Style.dimens.menubar_width + (width - Style.dimens.menubar_width) * position } else { navigation.leftOverlayMargin = 0 } diff --git a/resources/qml/Governikus/Navigation/+android/NavigationItem.qml b/resources/qml/Governikus/Navigation/+android/NavigationItem.qml index 69400bb..ec9ea47 100644 --- a/resources/qml/Governikus/Navigation/+android/NavigationItem.qml +++ b/resources/qml/Governikus/Navigation/+android/NavigationItem.qml @@ -1,35 +1,45 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 Item { property alias source: tabImage.source property alias text: tabText.text signal clicked + Accessible.role: Accessible.Button + Accessible.name: text + Item { id: tabImageItem height: parent.height - width: Constants.menubar_width + width: Style.dimens.menubar_width anchors.left: parent.left Image { id: tabImage - height: Utils.dp(35) + height: 35 + sourceSize.width: height fillMode: Image.PreserveAspectFit anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter } } - Text { + GText { id: tabText + anchors.left: tabImageItem.right - anchors.leftMargin: Utils.dp(10) anchors.verticalCenter: parent.verticalCenter - font.pixelSize: Constants.small_font_size - renderType: Text.NativeRendering - color: Constants.secondary_text + + Accessible.ignored: true + + textStyle: Style.text.hint_secondary } MouseArea { diff --git a/resources/qml/Governikus/Navigation/+android/NavigationView.qml b/resources/qml/Governikus/Navigation/+android/NavigationView.qml index 94f16c5..f0003f7 100644 --- a/resources/qml/Governikus/Navigation/+android/NavigationView.qml +++ b/resources/qml/Governikus/Navigation/+android/NavigationView.qml @@ -1,13 +1,20 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + Rectangle { id: content - height: appWindow.height - Constants.titlebar_height - width: Utils.dp(250) - color: Constants.background_color + height: appWindow.height - Style.dimens.titlebar_height + width: 250 + color: Style.color.background property var navigationController: null @@ -70,7 +77,7 @@ Rectangle { } - ListView { + GListView { id: listView anchors.fill: parent boundsBehavior: Flickable.StopAtBounds @@ -80,20 +87,24 @@ Rectangle { highlight: Rectangle { color: "black" opacity: 0.1 - height: Utils.dp(45) + height: 45 width: content.width y: listView.currentItem.y } highlightFollowsCurrentItem: false delegate: NavigationItem { - height: Utils.dp(45) + height: 45 width: content.width source: image - text: qsTr(desc) + settingsModel.translationTrigger + text: qsTr(desc) + SettingsModel.translationTrigger onClicked: { - navigationController.currentIndex = index - navigationController.state = condition + if (navigationController.state === condition) { + navigationController.reselectedState() + } else { + navigationController.currentIndex = index + navigationController.state = condition + } navigationController.close() } // Hide developer options if we are not using developer build (debug build) @@ -104,24 +115,30 @@ Rectangle { LocationButton { id: lang_de + anchors.margins: Constants.component_spacing + anchors.bottomMargin: plugin.safeAreaMargins.bottom + Constants.component_spacing + anchors.bottom: parent.bottom + anchors.right: lang_en.left + + Accessible.name: qsTr("Set language to german") + SettingsModel.translationTrigger + language: "de" name: "DE" image: "qrc:///images/location_flag_de.svg" - - anchors.margins: Constants.component_spacing - anchors.bottom: parent.bottom - anchors.right: lang_en.left } LocationButton { id: lang_en - language: "en" - name: "EN" - image: "qrc:///images/location_flag_en.svg" - anchors.margins: Constants.component_spacing anchors.bottom: parent.bottom anchors.right: parent.right + anchors.bottomMargin: plugin.safeAreaMargins.bottom + Constants.component_spacing + + Accessible.name: qsTr("Set language to english") + SettingsModel.translationTrigger + + language: "en" + name: "EN" + image: "qrc:///images/location_flag_en.svg" } } diff --git a/resources/qml/Governikus/Navigation/+ios/Navigation.qml b/resources/qml/Governikus/Navigation/+ios/Navigation.qml index 27b22bb..0975875 100644 --- a/resources/qml/Governikus/Navigation/+ios/Navigation.qml +++ b/resources/qml/Governikus/Navigation/+ios/Navigation.qml @@ -1,31 +1,46 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 -Item { - property bool lockedAndHidden: false +Rectangle { + id: baseItem + + property bool lockedAndHidden: true // Start in hidden state so that it doesn't slide out when the tutorial is active property bool isOpen: true property int currentIndex: 0 + property var bottomSafeAreaMargin: plugin.safeAreaMargins.bottom + + signal reselectedState + + function open() {} + + function close() {} + enabled: !lockedAndHidden + visible: !lockedAndHidden + height: lockedAndHidden ? 0 : (Style.dimens.tabbar_height + bottomSafeAreaMargin) - id: baseItem - state: "identify" + color: Constants.white + + Component.onCompleted: { + state = SettingsModel.showSetupAssistantOnStart ? "tutorial" : "identify" + } - height: childrenRect.height Behavior on height { - NumberAnimation {duration: 200} - } - - - function open() { - } - - function close() { + NumberAnimation {duration: Constants.animation_duration} } NavigationView { - visible: !baseItem.lockedAndHidden - height: visible ? Constants.tabbar_height : 0 + anchors.top: parent.top + height: Style.dimens.tabbar_height + + Accessible.ignored: lockedAndHidden } } diff --git a/resources/qml/Governikus/Navigation/+ios/NavigationItem.qml b/resources/qml/Governikus/Navigation/+ios/NavigationItem.qml index d196a6a..571874a 100644 --- a/resources/qml/Governikus/Navigation/+ios/NavigationItem.qml +++ b/resources/qml/Governikus/Navigation/+ios/NavigationItem.qml @@ -1,34 +1,53 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtGraphicalEffects 1.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 Item { property alias source: tabImage.source property alias text: tabText.text - property alias textColor: tabText.color + property var selected: false + property var iconPadding: 4 signal clicked + Accessible.role: Accessible.Button + Accessible.name: text + Accessible.onPressAction: clicked() + Image { id: tabImage - anchors.horizontalCenter: parent.horizontalCenter - anchors.topMargin: 5 anchors.top: parent.top anchors.bottom: tabText.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: iconPadding + fillMode: Image.PreserveAspectFit } - Text { + GText { id: tabText - color: Constants.secondary_text + anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom - anchors.bottomMargin: font.pixelSize / 10 - font.pixelSize: Constants.small_font_size - renderType: Text.NativeRendering + + Accessible.ignored: true + + textStyle: Style.text.hint } MouseArea { anchors.fill: parent onClicked: parent.clicked() } + + layer.enabled: true + layer.effect: ColorOverlay { + color: selected ? Style.color.accent : Constants.grey + } } diff --git a/resources/qml/Governikus/Navigation/+ios/NavigationView.qml b/resources/qml/Governikus/Navigation/+ios/NavigationView.qml index 7c0c84e..34cef19 100644 --- a/resources/qml/Governikus/Navigation/+ios/NavigationView.qml +++ b/resources/qml/Governikus/Navigation/+ios/NavigationView.qml @@ -1,61 +1,61 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtQuick.Layouts 1.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 -Rectangle { +Item { id: content + height: parent.height width: parent.width - color: Constants.background_color ListModel { id: navModel ListElement { - imageOn: "qrc:///images/iOS/tabBar/Ausweisen-on.png" - imageOff: "qrc:///images/iOS/tabBar/Ausweisen-off.png" + image: "qrc:///images/iOS/tabBar/ausweisen.svg" desc: QT_TR_NOOP("Identify") condition: "identify" } ListElement { - imageOn: "qrc:///images/iOS/tabBar/Anbieter-on.png" - imageOff: "qrc:///images/iOS/tabBar/Anbieter-off.png" + image: "qrc:///images/iOS/tabBar/anbieter.svg" desc: QT_TR_NOOP("Provider") condition: "provider" } ListElement { - imageOn: "qrc:///images/iOS/tabBar/Verlauf-on.png" - imageOff: "qrc:///images/iOS/tabBar/Verlauf-off.png" - desc: QT_TR_NOOP("History") - condition: "history" + image: "qrc:///images/iOS/tabBar/remoteleser.svg" + desc: QT_TR_NOOP("Remote") + condition: "remoteservice" } ListElement { - imageOn: "qrc:///images/iOS/tabBar/Pin-on.png" - imageOff: "qrc:///images/iOS/tabBar/Pin-off.png" - desc: QT_TR_NOOP("PIN Management") + image: "qrc:///images/iOS/tabBar/pin.svg" + desc: QT_TR_NOOP("PIN") condition: "pin" } ListElement { - imageOn: "qrc:///images/iOS/tabBar/More-on.svg" - imageOff: "qrc:///images/iOS/tabBar/More-off.svg" + image: "qrc:///images/iOS/tabBar/more.svg" desc: QT_TR_NOOP("More") condition: "more" } } - Rectangle { + GSeparator { id: topBorderLine width: parent.width - height: Utils.dp(0.5) - color: Constants.grey } - Row { + RowLayout { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom @@ -66,12 +66,21 @@ Rectangle { model: navModel delegate: NavigationItem { - height: parent.height - width: parent.width / navModel.count - source: baseItem.state === condition ? imageOn : imageOff - textColor: baseItem.state === condition ? Constants.blue : Constants.grey - text: qsTr(desc) + settingsModel.translationTrigger - onClicked: baseItem.state = condition + Layout.fillWidth: true + Layout.fillHeight: true + + Accessible.ignored: content.Accessible.ignored + + source: image + text: qsTr(desc) + SettingsModel.translationTrigger + selected: baseItem.state === condition + onClicked: { + if (baseItem.state === condition) { + baseItem.reselectedState() + } else { + baseItem.state = condition + } + } } } } diff --git a/resources/qml/Governikus/ProgressView/+desktop/ProgressView.qml b/resources/qml/Governikus/ProgressView/+desktop/ProgressView.qml index 4540fdd..33d764c 100644 --- a/resources/qml/Governikus/ProgressView/+desktop/ProgressView.qml +++ b/resources/qml/Governikus/ProgressView/+desktop/ProgressView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 @@ -6,11 +10,13 @@ import Governikus.Global 1.0 import Governikus.Style 1.0 import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 SectionPage { id: baseItem + property alias text: text.text property alias subText: subText.text property alias subTextColor: subText.color @@ -18,64 +24,97 @@ SectionPage property int progressValue property alias progressBarVisible: progressBar.visible + Accessible.name: qsTr("Progress view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the progress view of the AusweisApp2.") + SettingsModel.translationTrigger + StatusIcon { id: circle - height: ApplicationModel.scaleFactor * 400 + + height: Style.dimens.status_icon_large anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.top anchors.verticalCenterOffset: baseItem.height / 4 + busy: true source: "qrc:///images/desktop/sandglass.svg" } - Text { + GText { id: text + + visible: text.text !== "" + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.verticalCenter - width: parent.width - (2 * Constants.pane_padding) - font.pixelSize: Constants.header_font_size + Accessible.role: Accessible.Paragraph + activeFocusOnTab: true + Accessible.name: text.text + horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - color: Constants.white + textStyle: Style.text.header + onLinkActivated: Qt.openUrlExternally(link) + + FocusFrame {} } - Text { + GText { id: subText + + visible: subText.text !== "" + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) anchors.horizontalCenter: parent.horizontalCenter anchors.top: text.bottom - width: parent.width - (2 * Constants.pane_padding) + anchors.topMargin: Constants.text_spacing + + Accessible.role: Accessible.Paragraph + activeFocusOnTab: true + Accessible.name: subText.text - font.pixelSize: Constants.header_font_size horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - color: Constants.secondary_text + textStyle: Style.text.header_secondary + onLinkActivated: Qt.openUrlExternally(link) + + FocusFrame {} } - Text { + GText { id: progressText - horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.normal_font_size + + visible: progressText.text !== "" + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) anchors.bottom: progressBar.top anchors.bottomMargin: ApplicationModel.scaleFactor * 20 anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - (2 * Constants.pane_padding) - wrapMode: Text.WordWrap - color: Constants.white + + Accessible.role: Accessible.Paragraph + activeFocusOnTab: true + Accessible.name: progressText.text + + horizontalAlignment: Text.AlignHCenter + textStyle: Style.text.normal + + FocusFrame {} } ProgressBar { id: progressBar + + visible: false + height: ApplicationModel.scaleFactor * 40 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right anchors.margins: ApplicationModel.scaleFactor * 80 - height: ApplicationModel.scaleFactor * 40 + + Accessible.role: Accessible.ProgressBar + activeFocusOnTab: true + Accessible.name: qsTr("Step %1 of %2").arg(value).arg(to) + SettingsModel.translationTrigger + from: 0 - to: 5 - visible: false + to: 100 value: progressValue background: Rectangle { @@ -91,5 +130,7 @@ SectionPage color: Constants.green } } + + FocusFrame {} } } diff --git a/resources/qml/Governikus/ProgressView/+mobile/ProgressView.qml b/resources/qml/Governikus/ProgressView/+mobile/ProgressView.qml index 7a35846..6d51981 100644 --- a/resources/qml/Governikus/ProgressView/+mobile/ProgressView.qml +++ b/resources/qml/Governikus/ProgressView/+mobile/ProgressView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 @@ -32,54 +36,47 @@ SectionPage radius: width / 2 color: Constants.blue } - Text { + GText { id: text anchors.top: circle.bottom - anchors.topMargin: Utils.dp(50) + anchors.topMargin: 50 anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: Constants.header_font_size - font.weight: Font.Bold - color: Constants.blue horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap width: baseItem.width * 0.8 + textStyle: Style.text.header_accent } - Text { + GText { id: subText - color: Constants.secondary_text horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.normal_font_size anchors.top: text.bottom - anchors.topMargin: Utils.dp(10) + anchors.topMargin: 10 anchors.horizontalCenter: parent.horizontalCenter width: baseItem.width * 0.8 - wrapMode: Text.WordWrap + textStyle: Style.text.normal_secondary } - Text { + GText { id: progressText horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.normal_font_size anchors.top: subText.bottom - anchors.topMargin: Utils.dp(20) + anchors.topMargin: 20 anchors.horizontalCenter: parent.horizontalCenter width: baseItem.width * 0.8 - wrapMode: Text.WordWrap - color: Constants.grey + textStyle: Style.text.normal_secondary } ProgressBar { id: progressBar anchors.top: progressText.bottom anchors.horizontalCenter: parent.horizontalCenter - anchors.topMargin: Utils.dp(10) + anchors.topMargin: 10 width: baseItem.width * 0.6 - height: Utils.dp(24) + height: 24 from: 0 - to: 5 + to: 100 visible: false value: progressValue background: Rectangle { - radius: Utils.dp(2) + radius: 2 color: Constants.lightgrey } @@ -87,7 +84,7 @@ SectionPage Rectangle { width: progressBar.visualPosition * parent.width height: parent.height - radius: Utils.dp(2) + radius: 2 color: Constants.green } } diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderContactInfo.qml b/resources/qml/Governikus/Provider/+desktop/ProviderContactInfo.qml new file mode 100644 index 0000000..7a63a9f --- /dev/null +++ b/resources/qml/Governikus/Provider/+desktop/ProviderContactInfo.qml @@ -0,0 +1,72 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.View 1.0 + + +Item { + id: baseItem + + property alias contactModel: repeater.model + + height: columnLayout.height + + Accessible.name: qsTr("Provider contact information") + SettingsModel.translationTrigger + Accessible.description: qsTr("Contact information of the selected service provider.") + SettingsModel.translationTrigger + Accessible.role: Accessible.Heading + + ColumnLayout { + id: columnLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Constants.text_spacing + + GText { + bottomPadding: Constants.groupbox_spacing + + //: LABEL DESKTOP_QML + text: qsTr("Contact") + SettingsModel.translationTrigger + textStyle: Style.text.title + } + + Repeater { + id: repeater + + ColumnLayout { + Layout.fillWidth: true + + spacing: columnLayout.spacing + + GSeparator { + visible: index !== 0 + Layout.fillWidth: true + } + + ProviderContactInfoItem { + id: contactItem + + Layout.fillWidth: true + + imageSource: Qt.resolvedUrl(model.iconSource) + //: LABEL DESKTOP_QML + itemText: (!!model.text ? model.text : qsTr("Unknown")) + SettingsModel.translationTrigger + //: LABEL DESKTOP_QML + accessibleText: (!!model.accessibleText ? model.accessibleText : qsTr("Unknown")) + SettingsModel.translationTrigger + label: model.label + link: model.link + } + } + } + } +} diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderContactInfoItem.qml b/resources/qml/Governikus/Provider/+desktop/ProviderContactInfoItem.qml new file mode 100644 index 0000000..9cce98a --- /dev/null +++ b/resources/qml/Governikus/Provider/+desktop/ProviderContactInfoItem.qml @@ -0,0 +1,62 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.View 1.0 + + +Item { + id: baseItem + + property alias imageSource: image.source + property alias itemText: text.text + property string accessibleText + property url link + property string label + + implicitHeight: layout.height + + Accessible.name: label + Accessible.description: accessibleText + Accessible.role: Accessible.ListItem + + activeFocusOnTab: true + + RowLayout { + id: layout + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Constants.groupbox_spacing + + Image { + id: image + + sourceSize.height: ApplicationModel.scaleFactor * 40 + sourceSize.width: ApplicationModel.scaleFactor * 40 + fillMode: Image.PreserveAspectFit + } + + GText { + id: text + + Layout.fillWidth: true + + linkColor: color + + onLinkActivated: Qt.openUrlExternally(link) + } + } + + FocusFrame { + dynamic: false + framee: layout + } +} diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderDetailButtonBar.qml b/resources/qml/Governikus/Provider/+desktop/ProviderDetailButtonBar.qml new file mode 100644 index 0000000..baefda3 --- /dev/null +++ b/resources/qml/Governikus/Provider/+desktop/ProviderDetailButtonBar.qml @@ -0,0 +1,91 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.View 1.0 + + +Rectangle { + id: baseItem + + height: button.height + 2 * Constants.pane_padding + width: parent.width + + property string selectedCategory: "" + property string providerIcon: "" + property string address: "" + property string shortName: "" + property string shortDescription: "" + property alias buttonColor: button.buttonColor + + function clickButton() { + if (baseItem.address !== "") { + Qt.openUrlExternally(baseItem.address) + } + } + + Accessible.name: qsTr("Link to service provider") + SettingsModel.translationTrigger + Accessible.description: qsTr("Clicking this link will open the website of the service provider in your web browser. The URL of the provider is") + " " + address + SettingsModel.translationTrigger + Accessible.role: Accessible.Button + activeFocusOnTab: true + + Keys.onSpacePressed: clickButton() + + color: Constants.white + + Image { + id: icon + + anchors.left: parent.left + anchors.leftMargin: Constants.component_spacing + anchors.verticalCenter: baseItem.top + + height: ApplicationModel.scaleFactor * 135 + width: height + + source: baseItem.providerIcon + asynchronous: true + fillMode: Image.PreserveAspectFit + } + + GText { + width: parent.width / 2 + anchors.left: icon.right + anchors.right: button.left + anchors.leftMargin: Constants.component_spacing + anchors.rightMargin: Constants.component_spacing + anchors.verticalCenter: parent.verticalCenter + + textStyle: Style.text.header_inverse + text: shortDescription !== "" ? shortDescription : shortName + elide: Text.ElideRight + maximumLineCount: 2 + } + + GButton { + id: button + + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: Constants.pane_padding + + //: LABEL DESKTOP_QML + text: qsTr("To online application") + SettingsModel.translationTrigger + icon.source: providerIcon + buttonColor: categoryColor + enabled: baseItem.address !== "" + onClicked: baseItem.clickButton() + + ToolTip.delay: 500 + ToolTip.visible: hovered + ToolTip.text: baseItem.address + } + +} diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderDetailHistory.qml b/resources/qml/Governikus/Provider/+desktop/ProviderDetailHistory.qml new file mode 100644 index 0000000..0f2e93d --- /dev/null +++ b/resources/qml/Governikus/Provider/+desktop/ProviderDetailHistory.qml @@ -0,0 +1,78 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Provider 1.0 +import Governikus.Type.HistoryModel 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.View 1.0 + +Item { + id: baseItem + + height: columnLayout.height + + Accessible.name: qsTr("List of your past interactions with this provider") + SettingsModel.translationTrigger + Accessible.description: HistoryModel.nameFilter.count === 0 ? qsTr("The list is empty, no recorded interaction with this service provider.") + SettingsModel.translationTrigger : "" + Accessible.role: Accessible.List + + ColumnLayout { + id: columnLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Constants.text_spacing + + Repeater { + id: repeater + + model: HistoryModel.nameFilter + + ColumnLayout { + Layout.fillWidth: true + + spacing: columnLayout.spacing + + GSeparator { + visible: index !== 0 + Layout.fillWidth: true + } + + ProviderDetailHistoryItem { + id: historyItem + + Layout.fillWidth: true + + activeFocusOnTab: true + + providerName: subject + purposeText: purpose + } + + } + } + + GText { + id: textNoHistoryEntries + + visible: repeater.count === 0 + width: parent.width + + Accessible.name: text + activeFocusOnTab: true + + textStyle: Style.text.normal_secondary_inverse + //: INFO DESKTOP_QML No authentication history, placeholder text. + text: qsTr("Currently there are no history entries.") + SettingsModel.translationTrigger + } + } +} diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderDetailHistoryItem.qml b/resources/qml/Governikus/Provider/+desktop/ProviderDetailHistoryItem.qml new file mode 100644 index 0000000..2a5c6dc --- /dev/null +++ b/resources/qml/Governikus/Provider/+desktop/ProviderDetailHistoryItem.qml @@ -0,0 +1,82 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + + +Item { + id: baseItem + + property string providerName: "" + property string purposeText: "" + + height: columnLayout.height + + ColumnLayout { + id: columnLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Constants.text_spacing + + GText { + id: date + + textStyle: Style.text.normal_inverse + font.capitalization: Font.AllUppercase + text: ( Utils.isToday(dateTime) ? qsTr("today") : + Utils.isYesterday(dateTime) ? qsTr("yesterday") : + Utils.isThisWeek(dateTime) ? dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : + dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) + ) + SettingsModel.translationTrigger + } + + Row { + Layout.fillWidth: true + + GText { + width: parent.width * 0.25 + + textStyle: Style.text.normal_inverse + //: LABEL DESKTOP_QML + text: qsTr("Service:") + SettingsModel.translationTrigger + font.weight: Font.Bold + } + + GText { + width: parent.width * 0.75 + + textStyle: Style.text.normal_inverse + text: purposeText + } + } + + Row { + Layout.fillWidth: true + + GText { + width: parent.width * 0.25 + + textStyle: Style.text.normal_inverse + //: LABEL DESKTOP_QML + text: qsTr("Provider:") + SettingsModel.translationTrigger + font.weight: Font.Bold + } + + GText { + width: parent.width * 0.75 + + textStyle: Style.text.normal_inverse + text: providerName + } + } + } +} diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderDetailView.qml b/resources/qml/Governikus/Provider/+desktop/ProviderDetailView.qml index 08d62f0..b98426d 100644 --- a/resources/qml/Governikus/Provider/+desktop/ProviderDetailView.qml +++ b/resources/qml/Governikus/Provider/+desktop/ProviderDetailView.qml @@ -1,22 +1,182 @@ -import QtQuick 2.10 +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ -import Governikus.View 1.0 +import QtQuick 2.0 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 -import Governikus.Provider 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 SectionPage { + id: baseItem + + readonly property color titleBarColor: Category.displayColor(provider.category) + readonly property real titleBarOpacity: 1 + signal showDetailView(var pModel) + property alias historyModelItem: provider.modelItem + property alias providerModelItem: provider.modelItem + + Accessible.name: qsTr("Provider detail view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This view shows a detailed description of a service provider.") + SettingsModel.translationTrigger + titleBarAction: TitleBarAction { - text: qsTr("Provider details") - showSettings: false + text: provider.shortName helpTopic: "providerPage" } - Rectangle { - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - height: parent.height / 2 - width: parent.width / 2 + ProviderModelItem { + id: provider + } - color: "mediumaquamarine" + Item { + id: mainContent + + anchors.fill: parent + + Row { + id: imageHeader + + height: Math.floor(baseItem.height * 0.5) + width: parent.width + + anchors.top: parent.top + + Image { + id: image + + height: parent.height + width: Math.floor(baseItem.width * 0.6) + anchors.top: parent.top + + source: provider.image + sourceSize.height: height + asynchronous: true + fillMode: Image.PreserveAspectCrop + } + + Rectangle { + height: parent.height + width: Math.ceil(baseItem.width * 0.4) + + color: baseItem.titleBarColor + + ProviderContactInfo { + id: providerContactInfo + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Constants.pane_spacing + anchors.leftMargin: Constants.pane_spacing * 2 + anchors.rightMargin: Constants.pane_spacing * 2 + + activeFocusOnTab: true + + contactModel: provider.contactModel + } + + FocusFrame { + framee: providerContactInfo + scope: providerContactInfo + } + } + } + + ProviderDetailButtonBar { + id: buttonBar + + anchors.top: imageHeader.bottom + + selectedCategory: provider.category + providerIcon: provider.icon + address: provider.address + shortName: provider.shortName + shortDescription: provider.shortDescription + buttonColor: baseItem.titleBarColor + } + + RowLayout { + id: lowerRow + + anchors.top: buttonBar.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Constants.component_spacing + + spacing: Constants.component_spacing + + Item { + + Layout.fillHeight: true + Layout.fillWidth: true + + ScrollablePane { + id: leftPane + + anchors.fill: parent + + //: LABEL DESKTOP_QML + title: qsTr("Description") + SettingsModel.translationTrigger + + GText { + id: leftColumn + + width: parent.width + + Accessible.name: qsTr("Description of the service provider.") + SettingsModel.translationTrigger + Accessible.description: text + Accessible.role: Accessible.StaticText + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: !!provider.longDescription ? provider.longDescription : qsTr("The service provider did not provide a description.") + SettingsModel.translationTrigger + textStyle: Style.text.normal_inverse + } + } + + FocusFrame { + framee: leftPane + scope: leftColumn + } + } + + Item { + + Layout.fillHeight: true + Layout.fillWidth: true + + ScrollablePane { + id: rightPane + + anchors.fill: parent + + //: LABEL DESKTOP_QML + title: qsTr("History") + SettingsModel.translationTrigger + + ProviderDetailHistory { + id: rightColumn + + width: parent.width + + activeFocusOnTab: true + } + } + + FocusFrame { + framee: rightPane + scope: rightColumn + } + } + } + } + + FocusPoint { + color: Constants.black } } diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderInfoSection.qml b/resources/qml/Governikus/Provider/+desktop/ProviderInfoSection.qml index d90b346..6e1d220 100644 --- a/resources/qml/Governikus/Provider/+desktop/ProviderInfoSection.qml +++ b/resources/qml/Governikus/Provider/+desktop/ProviderInfoSection.qml @@ -1,25 +1,46 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 -Row { +Item { property alias image: icon.source property alias title: text.label property string name: "" - height: text.height - spacing: Constants.groupbox_spacing + Accessible.role: Accessible.Section + Accessible.name: text.Accessible.name - Image { - id: icon - sourceSize.height: ApplicationModel.scaleFactor * 40 - anchors.verticalCenter: text.verticalCenter + height: contentRow.height + width: contentRow.width + + Row { + id: contentRow + height: text.height + spacing: Constants.groupbox_spacing + + Image { + id: icon + sourceSize.height: ApplicationModel.scaleFactor * 40 + anchors.verticalCenter: text.verticalCenter + } + + LabeledText { + id: text + //: LABEL DESKTOP_QML + text: name.length > 0 ? name : qsTr("See details under \"more...\"") + SettingsModel.translationTrigger + } } - LabeledText { - id: text - text: name.length > 0 ? name : qsTr("See details under \"more...\"") + settingsModel.translationTrigger + FocusFrame { + border.color: Constants.black + dynamic: false } } diff --git a/resources/qml/Governikus/Provider/+desktop/ProviderModelItem.qml b/resources/qml/Governikus/Provider/+desktop/ProviderModelItem.qml new file mode 100644 index 0000000..2c3628f --- /dev/null +++ b/resources/qml/Governikus/Provider/+desktop/ProviderModelItem.qml @@ -0,0 +1,111 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + + +/* + * Convenience utility to access properties of a ProviderModel item + * This ensures having always a defined string, i.e. a non-null string object. + */ +Item { + id: baseItem + property var modelItem + + readonly property string category: !!modelItem && !!modelItem.providerCategory ? modelItem.providerCategory : "" + readonly property string shortName: !!modelItem && !!modelItem.providerShortName ? modelItem.providerShortName : "" + readonly property string longName: !!modelItem && !!modelItem.providerLongName ? modelItem.providerLongName : "" + readonly property string shortDescription: !!modelItem && !!modelItem.providerShortDescription ? modelItem.providerShortDescription : "" + readonly property string longDescription: !!modelItem && !!modelItem.providerLongDescription ? modelItem.providerLongDescription : "" + readonly property string address: !!modelItem && !!modelItem.providerAddress ? modelItem.providerAddress : "" + readonly property string addressDomain: !!modelItem && !!modelItem.providerAddressDomain ? modelItem.providerAddressDomain : "" + readonly property string homepage: !!modelItem && !!modelItem.providerHomepage ? modelItem.providerHomepage : "" + readonly property string homepageBase: !!modelItem && !!modelItem.providerHomepageBase ? modelItem.providerHomepageBase : "" + readonly property string phone: !!modelItem && !!modelItem.providerPhone ? modelItem.providerPhone : "" + readonly property string phoneCost: !!modelItem && !!modelItem.providerPhoneCost ? modelItem.providerPhoneCost : "" + readonly property string email: !!modelItem && !!modelItem.providerEmail ? modelItem.providerEmail : "" + readonly property string postalAddress: !!modelItem && !!modelItem.providerPostalAddress ? modelItem.providerPostalAddress : "" + readonly property string icon: !!modelItem && !!modelItem.providerIcon ? modelItem.providerIcon : "" + readonly property string image: !!modelItem && !!modelItem.providerImage ? modelItem.providerImage : "" + + readonly property ListModel contactModel: ListModel { + readonly property alias homepage: baseItem.homepage + readonly property alias email: baseItem.email + readonly property alias phone: baseItem.phone + readonly property alias phoneCost: baseItem.phoneCost + readonly property alias postalAddress: baseItem.postalAddress + + readonly property string phoneDisplayString: { + var s = "" + if (!!phone) { + s = '' + phone + "" + if (!!phoneCost) { + s += "
" + phoneCost + } + } + return s + } + + function removeHtml(htmlString) { + return htmlString.replace(/<\/?[a-z][a-z0-9]*[^>]*>/ig, " "); + } + + onHomepageChanged: { + setProperty(0, "text", !!homepage ? '' + homepage + "" : "") + setProperty(0, "accessibleText", !!homepage ? homepage : "") + setProperty(0, "link", homepage) + } + onEmailChanged: { + setProperty(1, "text", !!email ? '' + email + "" : "") + setProperty(1, "accessibleText", email ? email : "") + setProperty(1, "link", !!email ? "mailto:" + email : "") + } + onPhoneDisplayStringChanged: { + setProperty(2, "text", phoneDisplayString) + //: LABEL DESKTOP_QML + var coststring = !!phoneCost ? ", " + qsTr("Costs") + ": " + phoneCost : "" + setProperty(2, "accessibleText", !!phone ? phone + coststring : "") + setProperty(2, "link", !!phone? "tel:" + phone : "") + } + onPostalAddressChanged: { + var dest = 'https://www.google.com/maps?q=' + dest = !!postalAddress ? dest + encodeURIComponent(postalAddress.replace(//gi,' ')) : "" + setProperty(3, "text", !!postalAddress ? '' + postalAddress + "" : "") + setProperty(3, "accessibleText", !!postalAddress ? postalAddress : "") + setProperty(3, "link", dest) + } + + ListElement { + iconSource: "qrc:///images/provider/url.svg" + label: QT_TR_NOOP("Homepage") + text: "" + accessibleText: "" + link: "" + } + + ListElement { + iconSource: "qrc:///images/provider/mail.svg" + label: QT_TR_NOOP("E-Mail") + text: "" + accessibleTest: "" + link: "" + } + + ListElement { + iconSource: "qrc:///images/provider/telefon.svg" + label: QT_TR_NOOP("Phone") + text: "" + accessibleText: "" + link: "" + } + + ListElement { + iconSource: "qrc:///images/provider/adresse.svg" + label: QT_TR_NOOP("Contact") + text: "" + accessibleText: "" + link: "" + } + } +} diff --git a/resources/qml/Governikus/Provider/+mobile/+android/ProviderDetailTab.qml b/resources/qml/Governikus/Provider/+mobile/+android/ProviderDetailTab.qml index 66237b2..643b4e2 100644 --- a/resources/qml/Governikus/Provider/+mobile/+android/ProviderDetailTab.qml +++ b/resources/qml/Governikus/Provider/+mobile/+android/ProviderDetailTab.qml @@ -1,33 +1,41 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 TabButton { id: button - padding: Utils.dp(10) + padding: 10 + + Accessible.name: text + + contentItem: GText { + Accessible.ignored: true - contentItem: Text { text: button.text - font.pixelSize: Constants.small_font_size + textStyle: button.checked || button.pressed ? Style.text.hint_accent : Style.text.hint elide: Text.ElideRight - color: button.checked || button.pressed ? Constants.blue : Constants.black horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } background: Rectangle { - color: Constants.background_color + color: Style.color.background clip: true Rectangle { height: parent.height + radius width: parent.width - radius: Utils.dp(3) - color: button.checked ? Constants.white : Constants.grey - border.color: Constants.grey - border.width: Utils.dp(1) + radius: 3 + color: button.checked ? Constants.white : Style.color.border + border.color: Style.color.border + border.width: Style.dimens.separator_size } } } diff --git a/resources/qml/Governikus/Provider/+mobile/+android/ProviderViewDelegate.qml b/resources/qml/Governikus/Provider/+mobile/+android/ProviderViewDelegate.qml deleted file mode 100644 index c06b088..0000000 --- a/resources/qml/Governikus/Provider/+mobile/+android/ProviderViewDelegate.qml +++ /dev/null @@ -1,126 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 -import Governikus.Style 1.0 - -Rectangle { - id: baseItem - width: parent.width - height: Constants.provider_section_height - color: Constants.background_color - clip: true - - Rectangle { - id: background - color: "white" - anchors.left: parent.left - anchors.right: parent.right - anchors.rightMargin: ProviderStyle.providerListItemRightMargin - anchors.top: parent.top - anchors.topMargin: ProviderStyle.providerListItemTopMargin - anchors.bottom: parent.bottom - anchors.bottomMargin: ProviderStyle.providerListItemBottomMargin - - Item { - property int childrenHeight: subjectText.height + addressText.height - - id: contentItem - height: Constants.provider_section_height - anchors.verticalCenter: parent.verticalCenter - - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: ProviderStyle.itemLeftMargin - anchors.rightMargin: Utils.dp(15) - - property int detailsLinkWidth: height / ProviderStyle.infoItemWidthFactor - - Text { - id: subjectText - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: detailsLink.left - - anchors.topMargin: (Constants.provider_section_height - contentItem.childrenHeight) / 3 - anchors.rightMargin: Utils.dp(5) - - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.normal_font_size - color: ProviderStyle.subjectTextColor - font.bold: ProviderStyle.subjectTextFontBold - elide: Text.ElideRight - text: display - } - - Text { - id: addressText - - anchors.top: subjectText.bottom - anchors.left: parent.left - anchors.right: detailsLink.left - - anchors.topMargin: (Constants.provider_section_height - contentItem.childrenHeight) / 3 - anchors.bottomMargin: (Constants.provider_section_height - contentItem.childrenHeight) / 3 - - verticalAlignment: Text.AlignVCenter - font.pixelSize: ProviderStyle.addressTextFontSize - color: ProviderStyle.addressTextColor - elide: Text.ElideRight - text: providerAddressDomain - } - - Item { - id: detailsLink - anchors.right: parent.right - anchors.verticalCenter: ProviderStyle.providerListDetailsLinkPosition === "top" ? - subjectText.verticalCenter : - parent.verticalCenter - - anchors.margins: Utils.dp(5) - height: parent.height - width: contentItem.detailsLinkWidth - - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - height: width - - border.color: ProviderStyle.providerListDetailsLinkBorder - border.width: 1 - radius: width - - color: ProviderStyle.providerListDetailsLinkBackground - - Text { - anchors.centerIn: parent - text: "i" - font.bold: ProviderStyle.providerListDetailsLinkBold - color: ProviderStyle.providerListDetailsLinkColor - } - } - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: { - providerDetailView.providerModelItem = model - firePush(providerDetailView) - } - } - - Rectangle { - width: parent.width - anchors.top: parent.bottom - anchors.topMargin: -height - anchors.leftMargin: ProviderStyle.itemLeftMargin - anchors.left: parent.left - anchors.right: parent.right - visible: ProviderStyle.providerListItemsHaveBorder - height: visible ? 1 : 0 - color: Constants.grey - } -} diff --git a/resources/qml/Governikus/Provider/+mobile/+ios/ProviderDetailTab.qml b/resources/qml/Governikus/Provider/+mobile/+ios/ProviderDetailTab.qml index 379f205..5fb671b 100644 --- a/resources/qml/Governikus/Provider/+mobile/+ios/ProviderDetailTab.qml +++ b/resources/qml/Governikus/Provider/+mobile/+ios/ProviderDetailTab.qml @@ -1,22 +1,28 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 TabButton { id: button - contentItem: Text { + Accessible.role: Accessible.PageTab + Accessible.name: text + + contentItem: GText { text: button.text - font.pixelSize: Constants.small_font_size - elide: Text.ElideRight - color: button.checked ? Constants.white : Constants.blue + textStyle: button.checked ? Style.text.hint_inverse : Style.text.hint_accent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } background: Rectangle { - color: Constants.background_color + color: Style.color.background clip: true Rectangle { @@ -25,10 +31,10 @@ TabButton { anchors.left: parent.left anchors.leftMargin: button.TabBar.index === 0 ? 0 : -radius - radius: Utils.dp(3) - color: button.checked ? Constants.blue : button.pressed ? Constants.blue_light : Constants.white + radius: 3 + color: button.checked ? Style.color.accent : (button.pressed ? Constants.lightgrey : Constants.white) border.color: Constants.blue - border.width: Utils.dp(1) + border.width: 1 } } } diff --git a/resources/qml/Governikus/Provider/+mobile/+ios/ProviderViewDelegate.qml b/resources/qml/Governikus/Provider/+mobile/+ios/ProviderViewDelegate.qml deleted file mode 100644 index 436e4b1..0000000 --- a/resources/qml/Governikus/Provider/+mobile/+ios/ProviderViewDelegate.qml +++ /dev/null @@ -1,86 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Rectangle { - id: baseItem - width: parent.width - height: Constants.provider_section_height - color: Constants.background_color - clip: true - - Rectangle { - id: background - color: "white" - anchors.left: parent.left - anchors.right: parent.right - anchors.rightMargin: Utils.dp(5) - anchors.top: parent.top - anchors.topMargin: Utils.dp(2) - anchors.bottom: parent.bottom - anchors.bottomMargin: Utils.dp(5) - - Item { - height: subjectText.height + addressText.height - anchors.verticalCenter: parent.verticalCenter - - anchors.left: parent.left - anchors.leftMargin: Utils.dp(5) - anchors.right: detailsLink.left - anchors.rightMargin: Utils.dp(15) - - Text { - id: subjectText - color: Constants.secondary_text - width: parent.width - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.normal_font_size - elide: Text.ElideRight - text: display - } - Text { - id: addressText - anchors.top: subjectText.bottom - width: parent.width - - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.small_font_size - color: Constants.blue_dark - elide: Text.ElideRight - text: providerAddressDomain - } - } - Item { - id: detailsLink - anchors.right: parent.right - anchors.margins: Utils.dp(5) - height: parent.height - width: parent.height / 2 - - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - height: width - - border.color: Constants.blue_dark - border.width: 1 - radius: width - - Text { - anchors.centerIn: parent - text: qsTr("i") + settingsModel.translationTrigger - color: parent.border.color - } - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: { - providerDetailView.providerModelItem = model - firePush(providerDetailView) - } - } -} diff --git a/resources/qml/Governikus/Provider/+mobile/+phone/ProviderDetailView.qml b/resources/qml/Governikus/Provider/+mobile/+phone/ProviderDetailView.qml index 3852621..9488d32 100644 --- a/resources/qml/Governikus/Provider/+mobile/+phone/ProviderDetailView.qml +++ b/resources/qml/Governikus/Provider/+mobile/+phone/ProviderDetailView.qml @@ -1,19 +1,28 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: provider.shortName } + readonly property real tabBarSpacing: Constants.is_layout_ios ? Constants.component_spacing : 0 + property alias providerModelItem: provider.modelItem + + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + title: provider.shortName titleBarColor: Category.displayColor(provider.category) - property alias providerModelItem: provider.modelItem ProviderModelItem { id: provider } @@ -25,13 +34,13 @@ SectionPage { } content: Item { - height: swipeBar.height + swipeViewBackground.height + Constants.component_spacing + height: swipeBar.height + swipeViewBackground.height + 2 * Constants.component_spacing + tabBarSpacing width: baseItem.width TabBar { id: swipeBar spacing: 0 - width: Constants.is_layout_android ? parent.width : Utils.dp(220) + width: Constants.is_layout_android ? parent.width : parent.width * 0.666 // Each tab of the 2 tabs gets 1/3 of the parent width, the rest ist padding height: descriptionButton.implicitHeight anchors.top: parent.top anchors.topMargin: Constants.component_spacing @@ -41,26 +50,30 @@ SectionPage { ProviderDetailTab { id: descriptionButton - text: qsTr("DESCRIPTION") + settingsModel.translationTrigger + //: LABEL ANDROID_PHONE IOS_PHONE + text: qsTr("DESCRIPTION") + SettingsModel.translationTrigger } ProviderDetailTab { id: contactButton - text: qsTr("CONTACT") + settingsModel.translationTrigger + //: LABEL ANDROID_PHONE IOS_PHONE + text: qsTr("CONTACT") + SettingsModel.translationTrigger } } Rectangle { id: swipeViewBackground - anchors.top: swipeBar.bottom - anchors.horizontalCenter: swipeBar.horizontalCenter - height: swipeView.height + 2 * Constants.component_spacing + + height: swipeView.height width: parent.width + anchors.top: swipeBar.bottom + anchors.topMargin: tabBarSpacing + anchors.horizontalCenter: swipeBar.horizontalCenter SwipeView { id: swipeView - height: Math.max(providerText.contentHeight, providerInfo.contentHeight) - anchors.margins: Constants.component_spacing + + height: Math.max(providerText.implicitHeight, providerInfo.implicitHeight) anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right @@ -68,16 +81,22 @@ SectionPage { currentIndex: swipeBar.currentIndex clip: true - Text { + GText { id: providerText - text: (!!provider.longDescription ? provider.longDescription : qsTr("Description not available")) + settingsModel.translationTrigger + + Accessible.onScrollDownAction: baseItem.scrollPageDown() + Accessible.onScrollUpAction: baseItem.scrollPageUp() + + padding: Constants.component_spacing + //: LABEL ANDROID_PHONE IOS_PHONE + text: (!!provider.longDescription ? provider.longDescription : qsTr("Description not available")) + SettingsModel.translationTrigger + textStyle: Style.text.normal horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size } ProviderContactTab { id: providerInfo + contactModel: provider.contactModel } } diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfo.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfo.qml index 3e14007..9058aea 100644 --- a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfo.qml +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfo.qml @@ -1,7 +1,14 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.2 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + Rectangle { id: baseItem @@ -31,11 +38,11 @@ Rectangle { onVisibleChanged: { info.resetSize(); info.approximateSize() } onChildrenRectChanged: info.approximateSize() - Text { - text: qsTr("Contact") + settingsModel.translationTrigger + GText { + //: LABEL ANDROID_TABLET IOS_TABLET + text: qsTr("Contact") + SettingsModel.translationTrigger + textStyle: Style.text.header_inverse padding: Constants.component_spacing - font.pixelSize: Constants.header_font_size - color: "white" } Rectangle { // The delegates have space in between which is @@ -43,7 +50,7 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right height: contactListView.height - color: "white" + color: Constants.white ListView { id: contactListView @@ -56,7 +63,8 @@ Rectangle { width: contactListView.width color: baseItem.color imageSource: Qt.resolvedUrl(model.iconSource) - itemText: (!!model.text ? model.text : qsTr("Unknown")) + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET IOS_TABLET + itemText: (!!model.text ? model.text : qsTr("Unknown")) + SettingsModel.translationTrigger link: model.link sizeRecudctor: info.sizeRecudctor } diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfoItem.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfoItem.qml index 5a40b98..2faa670 100644 --- a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfoItem.qml +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderContactInfoItem.qml @@ -1,6 +1,12 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 Rectangle { @@ -9,30 +15,33 @@ Rectangle { property alias itemText: text.text property url link property int sizeRecudctor: 0 - height: text.height + Utils.dp(20) + height: text.height + 20 + + Accessible.role: Accessible.Link + Accessible.name: ApplicationModel.stripHtmlTags(itemText) Image { id: image - width: Utils.dp(25) - baseItem.sizeRecudctor + width: 25 - baseItem.sizeRecudctor height: width anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter fillMode: Image.PreserveAspectFit } - Text { + GText { id: text + anchors.left: image.right - anchors.leftMargin: Utils.dp(10) + anchors.leftMargin: 10 anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - font.pixelSize: Constants.normal_font_size - baseItem.sizeRecudctor - color: "white" + linkColor: color - elide: Text.ElideRight - wrapMode: Text.WordWrap - } + textStyle: Style.text.normal_inverse + font.pixelSize: Style.text.normal_inverse.textSize - baseItem.sizeRecudctor + } MouseArea { anchors.fill: parent diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailButtonBar.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailButtonBar.qml index ff2d20d..f97acb5 100644 --- a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailButtonBar.qml +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailButtonBar.qml @@ -1,6 +1,12 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Type.SettingsModel 1.0 + Item { id: baseItem @@ -27,7 +33,8 @@ Item { GButton { id: button - text: qsTr("ONLINE APPLICATION") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET IOS_TABLET + text: qsTr("ONLINE APPLICATION") + SettingsModel.translationTrigger buttonColor: baseItem.titleBarColor maxWidth: parent.width - icon.width - 3 * Constants.component_spacing anchors.left: icon.right diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailDescription.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailDescription.qml index f73c135..bf38a83 100644 --- a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailDescription.qml +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailDescription.qml @@ -1,26 +1,42 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 Column { id: baseItem spacing: Constants.pane_spacing + signal scrollDescriptionUp() + signal scrollDescriptionDown() + property string description: "" - Text { - font.pixelSize: Constants.header_font_size - color: Constants.blue - text: qsTr("Description") + settingsModel.translationTrigger + GText { + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: LABEL ANDROID_TABLET IOS_TABLET + text: qsTr("Description") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent } - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { + width: parent.width + + Accessible.role: Accessible.StaticText + Accessible.name: text + Accessible.onScrollDownAction: baseItem.scrollDescriptionDown() + Accessible.onScrollUpAction: baseItem.scrollDescriptionUp() + horizontalAlignment: Text.AlignLeft text: baseItem.description - width: parent.width - wrapMode: Text.Wrap + textStyle: Style.text.normal_secondary } } diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistory.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistory.qml index 071989b..640b1de 100644 --- a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistory.qml +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistory.qml @@ -1,39 +1,51 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 +import Governikus.Type.HistoryModel 1.0 +import Governikus.Type.SettingsModel 1.0 + Column { id: baseItem spacing: Constants.pane_spacing + signal scrollHistoryUp() + signal scrollHistoryDown() + property var openHistoryInfoFunc: function() { } - readonly property int historyItemHeight: Utils.dp(66) - readonly property int historyItemMargin: Utils.dp(8) - - Text { + GText { id: headerText - font.pixelSize: Constants.header_font_size - color: Constants.blue - text: qsTr("History") + settingsModel.translationTrigger + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: LABEL ANDROID_TABLET IOS_TABLET + text: qsTr("History") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent } Repeater { - model: historyModel.nameFilter + model: HistoryModel.nameFilter ProviderDetailHistoryItem { - itemHeight: baseItem.historyItemHeight - itemMargin: baseItem.historyItemMargin - width: parent.width + Accessible.onScrollDownAction: baseItem.scrollHistoryDown() + Accessible.onScrollUpAction: baseItem.scrollHistoryUp() + providerName: subject providerPostalAddress: providerPostalAddress dateTime: model.dateTime - infoText: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET IOS_TABLET + infoText: qsTr("Purpose for reading out requested data") + SettingsModel.translationTrigger purposeText: purpose requestedDataText: requestedData termsOfUsageText: termsOfUsage diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryInfo.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryInfo.qml index 910f959..34ceaa0 100644 --- a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryInfo.qml +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryInfo.qml @@ -1,7 +1,14 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 +import Governikus.Type.SettingsModel 1.0 + Item { id: baseItem @@ -20,7 +27,7 @@ Item { opacity: 0.4 } - Flickable { + GFlickable { anchors.fill: baseItem anchors.margins: Constants.component_spacing contentHeight: infoRow.height @@ -53,22 +60,24 @@ Item { ProviderInfoSection { imageSource: "qrc:///images/provider/information.svg" - title: qsTr("Service provider") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET IOS_TABLET + title: qsTr("Service provider") + SettingsModel.translationTrigger name: baseItem.providerName } ProviderInfoSection { imageSource: "qrc:///images/provider/purpose.svg" - title: qsTr("Purpose for reading out requested data") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET IOS_TABLET + title: qsTr("Purpose for reading out requested data") + SettingsModel.translationTrigger name: baseItem.purposeText } - Text { + GText { id: readDataTitle width: parent.width - font.pixelSize: Constants.header_font_size - color: Constants.blue - text: qsTr("Read data") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET IOS_TABLET + text: qsTr("Read data") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent } Column { @@ -83,20 +92,19 @@ Item { Item { id: textItem - height: Utils.dp(32) + height: 32 width: infoTable.width Rectangle { anchors.fill: textItem - color: "white" + color: Constants.white } - Text { - color: Constants.secondary_text - text: modelData.trim() - + GText { anchors.verticalCenter: parent.verticalCenter - font.pixelSize: Constants.normal_font_size + + text: modelData.trim() + textStyle: Style.text.normal_secondary } } } @@ -121,22 +129,20 @@ Item { anchors.right: parent.right spacing: Constants.pane_spacing - Text { + GText { id: termsOfUsageTitle - text: qsTr("Terms of usage") + settingsModel.translationTrigger - font.pixelSize: Constants.header_font_size - color: Constants.blue + //: LABEL ANDROID_TABLET IOS_TABLET + text: qsTr("Terms of usage") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent } - Text { + GText { id: termsOfUsageTextItem - color: Constants.secondary_text + + width: parent.width text: baseItem.termsOfUsageText - width: parent.width - elide: Text.ElideRight - wrapMode: Text.Wrap - font.pixelSize: Constants.normal_font_size + textStyle: Style.text.normal_secondary } } } diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryItem.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryItem.qml index b443c93..0d39239 100644 --- a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryItem.qml +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailHistoryItem.qml @@ -1,14 +1,16 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 -Item { +ListItem { id: baseItem - readonly property color backgroundColor: Constants.blue - readonly property int iconWidth: Utils.dp(18) - property string providerName: "" property string providerPostalAddress: "" property var dateTime: "" @@ -16,89 +18,30 @@ Item { property string purposeText: "" property string requestedDataText: "" property string termsOfUsageText: "" - property int itemHeight: 0 - property int itemMargin: 0 - property int lineHeight: itemHeight / 3 property var openInfoFunction: function () {} - height: itemHeight + 2 * itemMargin + height: 72 - Rectangle { - anchors.fill: baseItem - color: "white" - } + Accessible.description: qsTr("Click to view details of history entry.") + SettingsModel.translationTrigger - Column { - id: textColumn + //: LABEL ANDROID IOS + headerText: (Utils.isToday(dateTime) ? qsTr("today") : + Utils.isYesterday(dateTime) ? qsTr("yesterday") : + Utils.isThisWeek(dateTime) ? dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : + dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) + ) + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + text: (!!providerName ? providerName : qsTr("Touch for more details")) + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + footerText: purposeText !== "" ? purposeText : qsTr("Touch for more details") + SettingsModel.translationTrigger + contentMarginLeft: 0 + contentMarginRight: 0 - height: baseItem.itemHeight - width: baseItem.width - baseItem.iconWidth - - anchors.left: baseItem.left - anchors.top: baseItem.top - anchors.topMargin: baseItem.itemMargin - - Text { - height: baseItem.lineHeight - verticalAlignment: Text.AlignVCenter - font.pixelSize: Constants.label_font_size - font.capitalization: Font.AllUppercase - color: Constants.blue - text: (!new Date(dateTime) ? "" : - Utils.isToday(dateTime) ? qsTr("today") : - Utils.isYesterday(dateTime) ? qsTr("yesterday") : - Utils.isThisWeek(dateTime) ? dateTime.toLocaleString(Qt.locale(), qsTr("dddd")) : - dateTime.toLocaleString(Qt.locale(), qsTr("dd.MM.yyyy")) - ) + settingsModel.translationTrigger - } - - LabeledText { - label: !!purposeText ? purposeText : "" - text: (!!providerName ? providerName : qsTr("Touch for more details")) + settingsModel.translationTrigger - width: parent.width - height: baseItem.lineHeight - } - } - - Item { - id: detailsLink - - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - height: width - width: iconWidth - - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - height: width - - border.color: ProviderStyle.providerListDetailsLinkBorder - border.width: 1 - radius: width - - color: ProviderStyle.providerListDetailsLinkBackground - Text { - anchors.centerIn: parent - - text: "i" - font.bold: ProviderStyle.providerListDetailsLinkBold - color: ProviderStyle.providerListDetailsLinkColor - } - } - } - - MouseArea { - anchors.fill: baseItem - onClicked: baseItem.openInfoFunction({ - providerName: baseItem.providerName, - providerPostalAddress: baseItem.providerPostalAddress, - purposeText: baseItem.purposeText, - requestedDataText: baseItem.requestedDataText, - termsOfUsageText: baseItem.termsOfUsageText - }) - } + onClicked: baseItem.openInfoFunction({ + providerName: baseItem.providerName, + providerPostalAddress: baseItem.providerPostalAddress, + purposeText: baseItem.purposeText, + requestedDataText: baseItem.requestedDataText, + termsOfUsageText: baseItem.termsOfUsageText + }) } diff --git a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailView.qml b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailView.qml index 9218779..a4eaf92 100644 --- a/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailView.qml +++ b/resources/qml/Governikus/Provider/+mobile/+tablet/ProviderDetailView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 @@ -7,7 +11,7 @@ import Governikus.View 1.0 SectionPage { id: baseItem - readonly property TitleBarAction leftTitleBarAction: TitleBarAction { + navigationAction: NavigationAction { state: "back" onClicked: { if (providerDetailsHistoryInfo.visible) { @@ -18,12 +22,9 @@ SectionPage { } } } - readonly property TitleBarAction headerTitleBarAction: TitleBarAction { - text: historyModelItem && historyModelItem.subject ? historyModelItem.subject : provider.shortName - font.bold: true - } - readonly property Item rightTitleBarAction: Item {} - readonly property color titleBarColor: Category.displayColor(provider.category) + title: historyModelItem && historyModelItem.subject ? historyModelItem.subject : provider.shortName + rightTitleBarAction: Item {} + titleBarColor: Category.displayColor(provider.category) readonly property real titleBarOpacity: 1 property alias historyModelItem: provider.modelItem @@ -116,11 +117,15 @@ SectionPage { ProviderDetailDescription { id: leftColumn + anchors.margins: 2 * Constants.pane_padding anchors.top: buttonBar.bottom anchors.left: parent.left anchors.right: parent.right + onScrollDescriptionDown: baseItem.scrollPageDown() + onScrollDescriptionUp: baseItem.scrollPageUp() + description: provider.longDescription } } @@ -138,6 +143,7 @@ SectionPage { ProviderDetailHistory { id: rightColumn + anchors.topMargin: 2 * Constants.pane_padding anchors.leftMargin: Constants.pane_padding anchors.rightMargin: Constants.pane_padding @@ -145,6 +151,9 @@ SectionPage { anchors.left: parent.left anchors.right: parent.right + onScrollHistoryDown: baseItem.scrollPageDown() + onScrollHistoryUp: baseItem.scrollPageUp() + openHistoryInfoFunc: baseItem.openHistoryInfoFunc } } diff --git a/resources/qml/Governikus/Provider/+mobile/ProviderContactTab.qml b/resources/qml/Governikus/Provider/+mobile/ProviderContactTab.qml index e2b7745..5433b13 100644 --- a/resources/qml/Governikus/Provider/+mobile/ProviderContactTab.qml +++ b/resources/qml/Governikus/Provider/+mobile/ProviderContactTab.qml @@ -1,62 +1,89 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.2 +import QtGraphicalEffects 1.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + Item { id: baseItem - property alias contactModel: infoList.model - readonly property int contentHeight: infoList.contentHeight + property alias contactModel: infoList.model + + implicitHeight: infoList.contentHeight + 2 * Constants.component_spacing ListView { id: infoList + anchors.fill: parent + anchors.margins: Constants.component_spacing + interactive: false delegate: Item { id: delegateItem + width: parent.width - height: Math.max(textItem.height, Utils.dp(50)) + height: Math.max(textItem.height, 50) + + Accessible.role: Accessible.ListItem + Accessible.name: ApplicationModel.stripHtmlTags(textItem.text) Image { id: imageItem - fillMode: Image.PreserveAspectFit - height: Utils.dp(24) - width: Utils.dp(24) + + anchors.left: parent.left - anchors.leftMargin: Utils.dp(15) anchors.verticalCenter: parent.verticalCenter + sourceSize.height: Style.dimens.small_icon_size + sourceSize.width: Style.dimens.small_icon_size + source: Qt.resolvedUrl(model.iconSource) + fillMode: Image.PreserveAspectFit + + ColorOverlay { + anchors.fill: imageItem + source: imageItem + color: Style.color.accent + } } - Text { + GText { id: textItem - color: Constants.secondary_text - text: !!model.text ? model.text : qsTr("Unknown") + settingsModel.translationTrigger - verticalAlignment: Text.AlignVCenter + anchors.left: imageItem.right - anchors.leftMargin: Utils.dp(20) + anchors.leftMargin: Constants.component_spacing anchors.right: parent.right - anchors.rightMargin: Utils.dp(10) anchors.verticalCenter: parent.verticalCenter - font.pixelSize: Utils.dp(16) - wrapMode: Text.WordWrap + + Accessible.ignored: true + textFormat: Text.RichText + text: !!model.text ? model.text : qsTr("Unknown") + SettingsModel.translationTrigger + verticalAlignment: Text.AlignVCenter + textStyle: Style.text.normal } MouseArea { anchors.fill: delegateItem + enabled: !!model.link + onClicked: Qt.openUrlExternally(model.link) } - Rectangle { + GSeparator { + visible: index !== infoList.count - 1 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right - height: 1 - color: Constants.grey } } } diff --git a/resources/qml/Governikus/Provider/+mobile/ProviderHeader.qml b/resources/qml/Governikus/Provider/+mobile/ProviderHeader.qml index f10ce5a..63bb464 100644 --- a/resources/qml/Governikus/Provider/+mobile/ProviderHeader.qml +++ b/resources/qml/Governikus/Provider/+mobile/ProviderHeader.qml @@ -1,8 +1,15 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ProviderCategoryFilterModel 1.0 + Rectangle { id: baseItem @@ -12,6 +19,7 @@ Rectangle { // This is interpreted by the SectionPage component readonly property real titleBarOpacity: shadow.opacity === 1 ? 1 : (customProviderImage ? Math.max(0, 0.5 - shadow.opacity) : 0) + readonly property real titleBarTopBounce: sectionPageFlickable.verticalOvershoot < 0.0 ? -sectionPageFlickable.verticalOvershoot : 0.0 // Internal vars readonly property color shadowColor: Category.displayColor(selectedCategory) @@ -19,49 +27,35 @@ Rectangle { readonly property string backgroundImage: customProviderImage ? selectedProvider.image : Category.backgroundImageSource(selectedCategory) readonly property string categoryIcon: selectedProvider || selectedCategory !== "all" ? "" : Category.imageSource(selectedCategory) readonly property bool withButtons: selectedCategory === "" && !selectedProvider + readonly property real shadowOpacity: Math.min(1, headerOffsetY / (backgroundImage.height - Style.dimens.titlebar_height)) readonly property double iconHeightRatio: 0.3 readonly property double iconVerticalMarginRatio: 0.2 + // For some reason contentY is sometimes set to undefined. + readonly property real headerOffsetY: typeof(sectionPageFlickable.contentY) === "undefined" ? 0 : sectionPageFlickable.contentY + property int maxContentY: if (withButtons && parent != null) { return parent.height * (iconHeightRatio + iconVerticalMarginRatio) } else { return height / 2 } - height: backgroundImage.height + providerInfo.height + height: backgroundImage.height + providerInfo.height - titleBarTopBounce - function definedContentY() { - // For some reason contentY is sometimes set to undefined. - return typeof(contentY) === "undefined" ? 0 : contentY - } - - function currentMargin() { - // Height of button icons. - var H = height * iconHeightRatio - - // Initial inferior margin for button icons. - var M = height * iconVerticalMarginRatio - - var y = definedContentY() - - return -2 * y * (M + H) / (3 * M + 2 * H) + M - } + color: providerInfo.color // Some provider images have transparent backgrounds, so we need to fill the background Image { id: backgroundImage source: baseItem.backgroundImage - height: width / 1.80 + height: width / 1.80 + titleBarTopBounce width: parent.width anchors.left: parent.left anchors.top: parent.top + anchors.topMargin: -titleBarTopBounce // When flicking over the top, scale the image (similar to native iOS apps) fillMode: Image.PreserveAspectCrop - function transition() { - return Math.min(1, contentY / (height - Constants.titlebar_height)) - } - Image { id: categoryIcon source: baseItem.categoryIcon @@ -75,7 +69,7 @@ Rectangle { visible: baseItem.categoryIcon !== "" - opacity: baseItem.definedContentY() <= maxContentY ? 1 : 0 + opacity: baseItem.headerOffsetY <= maxContentY ? 1 : 0 Behavior on opacity { NumberAnimation {} } @@ -84,7 +78,7 @@ Rectangle { Image { source: selectedProvider ? selectedProvider.icon : "" asynchronous: true - height: Utils.dp(70) + height: 70 width: height fillMode: Image.PreserveAspectFit anchors.margins: Constants.component_spacing @@ -92,13 +86,6 @@ Rectangle { anchors.bottom: parent.bottom visible: !!selectedProvider } - - Rectangle { - id: shadow - anchors.fill: parent - color: baseItem.shadowColor - opacity: parent.transition() - } } Row { @@ -111,15 +98,14 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: backgroundImage.bottom - anchors.bottomMargin: baseItem.currentMargin() + anchors.bottomMargin: backgroundImage.height / 2 - iconsRow.height - titleBarTopBounce - headerOffsetY / (headerOffsetY < 0.0 ? 1.0 : 2.0) Repeater { model: ["citizen", "finance", "insurance", "other"] - Rectangle { + Item { height: parent.height width: parent.width * 0.25 - color: "transparent" Image { source: Category.buttonImageSource(modelData) @@ -136,6 +122,15 @@ Rectangle { } } + Rectangle { + id: shadow + + anchors.fill: backgroundImage + + color: baseItem.shadowColor + opacity: shadowOpacity + } + Rectangle { id: providerInfo height: visible ? column.height + 2 * Constants.pane_padding : 0 @@ -153,13 +148,11 @@ Rectangle { anchors.right: parent.right spacing: Constants.pane_spacing - Text { + GText { id: providerText - color: Constants.secondary_text width: parent.width text: selectedProvider ? selectedProvider.shortDescription : "" - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap + textStyle: Style.text.normal visible: text.length > 0 } @@ -167,7 +160,8 @@ Rectangle { id: providerButton anchors.right: parent.right buttonColor: shadowColor - text: qsTr("To service provider") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("To service provider") + SettingsModel.translationTrigger onClicked: { Qt.openUrlExternally(selectedProvider ? selectedProvider.address : "") } diff --git a/resources/qml/Governikus/Provider/+mobile/ProviderInfoSection.qml b/resources/qml/Governikus/Provider/+mobile/ProviderInfoSection.qml index c25f45f..dc112d4 100644 --- a/resources/qml/Governikus/Provider/+mobile/ProviderInfoSection.qml +++ b/resources/qml/Governikus/Provider/+mobile/ProviderInfoSection.qml @@ -1,6 +1,11 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Type.SettingsModel 1.0 Rectangle { @@ -12,7 +17,7 @@ Rectangle { width: parent.width height: Math.max(image.height, providerTitle.height) - color: "white" + color: Constants.white clip: true Image { @@ -20,8 +25,8 @@ Rectangle { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter - height: Utils.dp(40) - width: Utils.dp(40) + height: 40 + width: 40 source: imageSource fillMode: Image.PreserveAspectFit @@ -31,10 +36,11 @@ Rectangle { id: providerTitle anchors.verticalCenter: image.verticalCenter anchors.left: image.right - anchors.leftMargin: Utils.dp(10) + anchors.leftMargin: 10 anchors.right: parent.right label: title - text: name.length > 0 ? name : qsTr("Touch for more details") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: name.length > 0 ? name : qsTr("Touch for more details") + SettingsModel.translationTrigger } } diff --git a/resources/qml/Governikus/Provider/+mobile/ProviderModelItem.qml b/resources/qml/Governikus/Provider/+mobile/ProviderModelItem.qml index 9d7f2cf..2aabdae 100644 --- a/resources/qml/Governikus/Provider/+mobile/ProviderModelItem.qml +++ b/resources/qml/Governikus/Provider/+mobile/ProviderModelItem.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 @@ -76,28 +80,28 @@ Item { } ListElement { - iconSource: "qrc:///images/provider/url.png" + iconSource: "qrc:///images/provider/url.svg" label: QT_TR_NOOP("Homepage") text: "" link: "" } ListElement { - iconSource: "qrc:///images/provider/mail.png" + iconSource: "qrc:///images/provider/mail.svg" label: QT_TR_NOOP("E-Mail") text: "" link: "" } ListElement { - iconSource: "qrc:///images/provider/telefon.png" + iconSource: "qrc:///images/provider/telefon.svg" label: QT_TR_NOOP("Phone") text: "" link: "" } ListElement { - iconSource: "qrc:///images/provider/adresse.png" + iconSource: "qrc:///images/provider/adresse.svg" label: QT_TR_NOOP("Contact") text: "" link: "" diff --git a/resources/qml/Governikus/ProviderView/+desktop/AdditionalResultsItem.qml b/resources/qml/Governikus/ProviderView/+desktop/AdditionalResultsItem.qml new file mode 100644 index 0000000..5f1c922 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+desktop/AdditionalResultsItem.qml @@ -0,0 +1,101 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 + +Item { + id: baseItem + + property int totalHits: ProviderCategoryFilterModel.additionalResultCount + signal clicked() + + implicitHeight: row.implicitHeight + 2 * Constants.groupbox_spacing + + Keys.onSpacePressed: mouseArea.clicked(undefined) + Accessible.role: Accessible.Button + Accessible.name: qsTr("Additional results in other categories: %1. Click here to remove filter.").arg(totalHits) + SettingsModel.translationTrigger + + Item { + id: shadowLayer + + anchors.fill: parent + + layer.enabled: true + layer.effect: DropShadow { + radius: 4 + samples: 8 + source: background + color: Qt.darker(Constants.grey, 1.2) + } + } + + Rectangle { + id: background + + anchors.fill: parent + + color: Category.displayColor("all") + radius: Style.dimens.corner_radius + } + + RowLayout { + id: row + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Constants.groupbox_spacing + + spacing: Constants.groupbox_spacing + + Image { + id: icon + + sourceSize.height: ApplicationModel.scaleFactor * 60 + sourceSize.width: ApplicationModel.scaleFactor * 60 + + source: Category.imageSource("all") + asynchronous: true + fillMode: Image.PreserveAspectFit + } + + GText { + id: nameText + + verticalAlignment: Text.AlignVCenter + Layout.fillWidth: true + + //: LABEL DESKTOP_QML + text: qsTr("Additional results in other categories:") + " " + baseItem.totalHits + SettingsModel.translationTrigger + + textStyle: Style.text.normal + } + + GButton { + id: showButton + + text: qsTr("Show") + SettingsModel.translationTrigger + onClicked: baseItem.clicked() + } + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + + onClicked: baseItem.clicked() + } + + FocusFrame {} +} diff --git a/resources/qml/Governikus/ProviderView/+desktop/ProviderCard.qml b/resources/qml/Governikus/ProviderView/+desktop/ProviderCard.qml new file mode 100644 index 0000000..ef201fb --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+desktop/ProviderCard.qml @@ -0,0 +1,135 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Window 2.10 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Provider 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.View 1.0 + +Item { + id: baseItem + + property alias providerModelItem: provider.modelItem + signal showDetailView(var pModelItem) + + height: Math.floor(width * 0.84) // Set a fixed aspect ratio which best fits the view + + Keys.onSpacePressed: mouseArea.clicked(undefined) + Accessible.role: Accessible.Button + Accessible.name: qsTr("Provider:") + " " + (!!providerModelItem ? provider.shortName : qsTr("Unknown error")) + SettingsModel.translationTrigger + Accessible.description: qsTr("Provider description:") + " " + (!!providerModelItem ? provider.shortDescription : qsTr("Unknown error")) + SettingsModel.translationTrigger + + ProviderModelItem { + id: provider + } + + Column { + id: column + + width: baseItem.width + + Image { + id: image + + height: Math.floor(baseItem.width * 0.56) // Image aspect ratio 16:9 + width: baseItem.width + + source: provider.image + // Set a fixed size for width and height, so it doesn't have to resize the source when the window size changes -> way faster + sourceSize.width: Screen.devicePixelRatio * 512 + mipmap: true + asynchronous: true + fillMode: Image.PreserveAspectCrop + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Item { + width: image.width + height: image.height + RoundedRectangle { + anchors.centerIn: parent + width: image.width + height: image.height + radius: Style.dimens.corner_radius + bottomLeftCorner: false + bottomRightCorner: false + } + } + } + } + + Rectangle { + width: baseItem.width + height: Math.floor(baseItem.width * 0.2) + + color: Constants.white + + GText { + id: nameText + + anchors.fill: parent + anchors.leftMargin: Constants.text_spacing + anchors.rightMargin: Constants.text_spacing + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: provider.longName !== "" ? provider.longName : provider.shortName + + elide: Text.ElideRight + maximumLineCount: 2 + textStyle: Style.text.normal_inverse + } + } + + RoundedRectangle { + width: baseItem.width + height: Math.floor(baseItem.width * 0.08) + + radius: Style.dimens.corner_radius + color: Category.displayColor(provider.category) + topLeftCorner: false + topRightCorner: false + + GText { + id: providerText + + anchors.fill: parent + anchors.leftMargin: Constants.text_spacing + anchors.rightMargin: Constants.text_spacing + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: provider.homepageBase + + elide: Text.ElideRight + maximumLineCount: 1 + textStyle: Style.text.hint + } + } + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + + onClicked: baseItem.showDetailView(providerModelItem) + cursorShape: Qt.PointingHandCursor + } + + FocusFrame { + dynamic: false + marginFactor: 2 + radius: Style.dimens.corner_radius + border.color: Constants.black + } +} diff --git a/resources/qml/Governikus/ProviderView/+desktop/ProviderOverview.qml b/resources/qml/Governikus/ProviderView/+desktop/ProviderOverview.qml new file mode 100644 index 0000000..cae3bf0 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+desktop/ProviderOverview.qml @@ -0,0 +1,174 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 +import Governikus.Type.ApplicationModel 1.0 + +SectionPage { + id: baseItem + + signal showDetailView(var pModelItem) + + Accessible.name: qsTr("Provider view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the provider view of the AusweisApp2.") + SettingsModel.translationTrigger + + Component.onCompleted: { + ProviderCategoryFilterModel.setCategorySelection("all") + ProviderCategoryFilterModel.searchString = "" + tabbedPane.currentIndex = 0 + } + + TabbedPane { + id: tabbedPane + + anchors.fill: parent + anchors.margins: Constants.pane_padding + + sectionsModel: [ + { + //: LABEL DESKTOP_QML + categoryName: qsTr("All provider") + SettingsModel.translationTrigger, + category: "all" + }, + { + //: LABEL DESKTOP_QML + categoryName: qsTr("Citizen services") + SettingsModel.translationTrigger, + category: "citizen" + }, + { + //: LABEL DESKTOP_QML + categoryName: qsTr("Financials") + SettingsModel.translationTrigger, + category: "finance" + }, + { + //: LABEL DESKTOP_QML + categoryName: qsTr("Insurances") + SettingsModel.translationTrigger, + category: "insurance" + }, + { + //: LABEL DESKTOP_QML + categoryName: qsTr("Other services") + SettingsModel.translationTrigger, + category: "other" + } + ] + + sectionDelegate: TabbedPaneDelegateIconAndText { + sectionName: model.modelData.categoryName + iconPath: Category.imageSource(model.modelData.category) + } + contentDelegate: content + contentPadding: 0 + onCurrentItemModelChanged: { + if (currentItemModel === null) { + return + } + + ProviderCategoryFilterModel.setCategorySelection(currentItemModel.modelData.category) + } + + Component { + id: content + + Item { + height: tabbedPane.availableHeight + width: parent.width + + GridView { + id: gridView + + property int columns: Math.floor(width / (ApplicationModel.scaleFactor * 400)) + property bool hasResults: gridView.count > 0 || ProviderCategoryFilterModel.additionalResultCount > 0 + property real spacing: Constants.component_spacing + + anchors { + top: parent.top + left: parent.left + right: parent.right + bottom: additionalResults.top + topMargin: Math.floor(spacing / 2) + leftMargin: Math.floor(spacing / 2) + bottomMargin: Math.floor(spacing / 2) + } + + cellWidth: Math.floor((width - spacing / 2) / columns) + cellHeight: Math.floor(cellWidth * 0.84) // Set aspect ratio from ProviderCard + displayMarginBeginning: spacing + displayMarginEnd: additionalResults.height + spacing + highlightFollowsCurrentItem: true + highlight: null + activeFocusOnTab: true + + model: ProviderCategoryFilterModel + + boundsBehavior: Flickable.StopAtBounds + ScrollBar.vertical: ScrollBar { + policy: size === 1.0 ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn + } + + delegate: Item { + width: gridView.cellWidth + height: gridView.cellHeight + + ProviderCard { + anchors.fill: parent + anchors.margins: Math.floor(gridView.spacing / 2) + + focus: gridView.currentIndex === index + providerModelItem: model + + onShowDetailView: baseItem.showDetailView(pModelItem) + } + } + + Connections { + target: ProviderCategoryFilterModel + onFireCriteriaChanged: gridView.contentY = gridView.originY + } + } + + AdditionalResultsItem { + id: additionalResults + + visible: ProviderCategoryFilterModel.additionalResultCount > 0 && ProviderCategoryFilterModel.categories.length > 0 && ProviderCategoryFilterModel.categories.indexOf("all") === -1 + height: visible ? implicitHeight : 0 + width: gridView.columns * gridView.cellWidth + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + leftMargin: Math.floor(gridView.spacing * 2) + rightMargin: Math.floor(gridView.spacing * 2) + topMargin: visible ? gridView.spacing : 0 + bottomMargin: visible ? gridView.spacing : 0 + } + + activeFocusOnTab: true + + onActiveFocusChanged: { + if (activeFocus) { + gridView.positionViewAtIndex(index, GridView.Center) + } + } + + onClicked: { + ProviderCategoryFilterModel.setCategorySelection("all") + tabbedPane.currentIndex = 0 + } + } + } + } + } + + FocusPoint {} +} diff --git a/resources/qml/Governikus/ProviderView/+desktop/ProviderView.qml b/resources/qml/Governikus/ProviderView/+desktop/ProviderView.qml index 909cdd6..b8ca54a 100644 --- a/resources/qml/Governikus/ProviderView/+desktop/ProviderView.qml +++ b/resources/qml/Governikus/ProviderView/+desktop/ProviderView.qml @@ -1,51 +1,83 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtQuick.Controls 2.3 -import Governikus.View 1.0 -import Governikus.TitleBar 1.0 +import Governikus.Global 1.0 import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 import Governikus.Type.ApplicationModel 1.0 - +import Governikus.Type.HistoryModel 1.0 SectionPage { - id: sectionPage + id: baseItem - KeyNavigation.tab: navSuccessor - Accessible.role: Accessible.Grouping - Accessible.name: qsTr("Provider view") - Accessible.description: qsTr("This is the provider view of the AusweisApp2.") - - titleBarAction: TitleBarAction { - text: qsTr("Provider") - onClicked: { - rect.visible = true - detailView.visible = false - } - customSubAction: SearchBar { - availableWidth: ApplicationModel.scaleFactor * 768 - } - showHelp: false + enum SubViews { + None = 0, + Detail } - Rectangle { - id: rect - - anchors.fill: parent - color: "red" - opacity: 0.5 - visible: true - - MouseArea { - anchors.fill: parent - onClicked: { - rect.visible = false - detailView.visible = true - } + Keys.onEscapePressed: { + if (d.activeView === ProviderView.SubViews.None) { + event.accepted = false + return } + + d.activeView = ProviderView.SubViews.None + } + + isAbstract: true + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Provider") + SettingsModel.translationTrigger + helpTopic: "providerPage" + + onClicked: { + d.activeView = ProviderView.SubViews.None + } + + customSubAction: SearchBar { + onDisplayTextChanged: ProviderCategoryFilterModel.searchString = displayText + } + } + + QtObject { + id: d + + property int activeView: ProviderView.SubViews.None } ProviderDetailView { id: detailView - visible: false - onNextView: sectionPage.nextView(pName) + + visible: d.activeView === ProviderView.SubViews.Detail + + activeFocusOnTab: true + + onNextView: { + d.activeView = ProviderView.SubViews.None + baseItem.nextView(pName) + } + } + + ProviderOverview { + id: overviewView + + visible: d.activeView === ProviderView.SubViews.None + + activeFocusOnTab: true + + onNextView: baseItem.nextView(pName) + onShowDetailView: { + HistoryModel.nameFilter.setProviderAddress(pModelItem.providerAddress) + detailView.providerModelItem = pModelItem + d.activeView = ProviderView.SubViews.Detail + } } } diff --git a/resources/qml/Governikus/ProviderView/+desktop/SearchBar.qml b/resources/qml/Governikus/ProviderView/+desktop/SearchBar.qml deleted file mode 100644 index 17f272f..0000000 --- a/resources/qml/Governikus/ProviderView/+desktop/SearchBar.qml +++ /dev/null @@ -1,63 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 - - -Row { - id: root - - property int availableWidth: 0 - readonly property int contentWidth: root.implicitWidth - readonly property alias searchText: searchField.displayText - - spacing: Constants.titlebar_padding - height: searchField.height - - GTextField { - id: searchField - width: root.availableWidth - parent.spacing - iconItem.width - 2 * Constants.titlebar_padding - font.pixelSize: Constants.small_font_size - - visible: false - - onAccepted: { - iconItem.forceActiveFocus(Qt.MouseFocusReason) - } - - Behavior on visible { - PropertyAnimation { - duration: 150 - } - } - } - - Image { - id: iconItem - - sourceSize.height: parent.height - source: "qrc:///images/search.svg" - - MouseArea { - anchors.fill: parent - onClicked: { - // Storage of the new value is needed because the query - // of searchField.visible still delivers the old value. - var searchFieldVisible = !searchField.visible - - if (searchFieldVisible) { - iconItem.source = "qrc:///images/cancel.svg" - searchField.forceActiveFocus(Qt.MouseFocusReason) - Qt.inputMethod.show() - } else { - iconItem.source = "qrc:///images/search.svg" - iconItem.forceActiveFocus(Qt.MouseFocusReason) - searchField.text = "" - Qt.inputMethod.hide() - } - - searchField.visible = searchFieldVisible - } - } - } -} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+android/+phone/ProviderView.qml b/resources/qml/Governikus/ProviderView/+mobile/+android/+phone/ProviderView.qml index 84f1b6f..8cb9583 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+android/+phone/ProviderView.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/+android/+phone/ProviderView.qml @@ -1,114 +1,23 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 -import Governikus.Provider 1.0 +import Governikus.Style 1.0 import Governikus.View 1.0 import Governikus.Type.ProviderCategoryFilterModel 1.0 +import Governikus.Provider 1.0 -SectionPage { - id: baseItem - - readonly property var category: ProviderCategoryFilterModel.categories.length === 0 ? "" : ProviderCategoryFilterModel.categories[0] - - Component.onCompleted: ProviderCategoryFilterModel.sortByCategoryFirst(true) - - onCategoryChanged: { - ProviderCategoryFilterModel.sortByCategoryFirst(category === "") - } - - leftTitleBarAction: TitleBarAction { - state: category !== "" ? "back" : "" - onClicked: { - if (state === "back") { - ProviderCategoryFilterModel.setCategorySelection("") - } - } - } - - headerTitleBarAction: TitleBarAction { - text: Category.displayString(category) + settingsModel.translationTrigger - font.bold: true - } - +BaseProviderView { rightTitleBarAction: SearchBar { - availableWidth: baseItem.width - Constants.menubar_width + availableWidth: baseItem.width - Style.dimens.menubar_width onSearchTextChanged: ProviderCategoryFilterModel.searchString = searchText } - titleBarColor: Category.displayColor(category) - - ProviderDetailView { - id: providerDetailView - visible: false - } - header: ProviderHeader { width: baseItem.width selectedCategory: category } - - content: Column { - width: baseItem.width - - Rectangle { - height: Utils.dp(200) - width: parent.width - color: Constants.background_color - visible: ProviderCategoryFilterModel.rowCount === 0 && !additionalResults.visible - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("No match found") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - } - } - - ProviderSectionDelegate { - id: allSection - sectionName: "all" - visible: ProviderCategoryFilterModel.searchString === "" && ProviderCategoryFilterModel.categories.length === 0 - height: visible ? Constants.provider_section_height : 0 - } - - ListView { - id: providerListMain - height: childrenRect.height - width: baseItem.width - interactive: false - visible: category === "" - - model: ProviderCategoryFilterModel - - delegate: ProviderViewDelegate { - height: visible ? Constants.provider_section_height : 0 - visible: ProviderCategoryFilterModel.searchString !== "" - } - - section.property: "providerCategory" - section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart - section.delegate: ProviderSectionDelegate { - sectionName: section - } - } - - ListView { - id: providerListSection - height: childrenRect.height - width: baseItem.width - interactive: false - visible: !providerListMain.visible - - model: ProviderCategoryFilterModel - - delegate: ProviderViewDelegate {} - } - - AdditionalResultsItem { - id: additionalResults - width: parent.width - } - } } diff --git a/resources/qml/Governikus/ProviderView/+mobile/+android/+tablet/ProviderView.qml b/resources/qml/Governikus/ProviderView/+mobile/+android/+tablet/ProviderView.qml index badf8af..0e855e3 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+android/+tablet/ProviderView.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/+android/+tablet/ProviderView.qml @@ -1,16 +1,24 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.HistoryModel 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ProviderCategoryFilterModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction {} - headerTitleBarAction: TitleBarAction { text: qsTr("Provider") + settingsModel.translationTrigger; font.bold: true } + navigationAction: NavigationAction {} + title: qsTr("Provider") + SettingsModel.translationTrigger rightTitleBarAction: SearchBar { availableWidth: baseItem.width onSearchTextChanged: ProviderCategoryFilterModel.searchString = searchText @@ -22,8 +30,8 @@ SectionPage { property bool wasVisible: false onVisibleChanged: wasVisible = true - readonly property int headerHeight: Utils.dp(54) - readonly property int separatorHeight: Utils.dp(2) + readonly property int headerHeight: 54 + readonly property int separatorHeight: 2 ProviderDetailView { id: providerDetailView @@ -31,7 +39,7 @@ SectionPage { } function pushProviderDetails(pModel) { - historyModel.nameFilter.setProviderAddress(pModel.providerAddress) + HistoryModel.nameFilter.setProviderAddress(pModel.providerAddress) providerDetailView.providerModelItem = pModel firePush(providerDetailView) } @@ -46,7 +54,7 @@ SectionPage { height: baseItem.headerHeight width: parent.width - color: "white" + color: Constants.white Row { id: checkBoxesItem @@ -55,8 +63,8 @@ SectionPage { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - padding: Utils.dp(30) - spacing: Utils.dp(30) + padding: 30 + spacing: 30 transformOrigin: Item.Center scale: Math.min(parent.width / width, 1) @@ -66,7 +74,8 @@ SectionPage { category: "citizen" imageSource: Category.imageSource("citizen") - text: qsTr("Citizen services") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + text: qsTr("Citizen services") + SettingsModel.translationTrigger } CategoryCheckbox { @@ -74,7 +83,8 @@ SectionPage { category: "finance" imageSource: Category.imageSource("finance") - text: qsTr("Financials") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + text: qsTr("Financials") + SettingsModel.translationTrigger } CategoryCheckbox { @@ -82,7 +92,8 @@ SectionPage { category: "insurance" imageSource: Category.imageSource("insurance") - text: qsTr("Insurances") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + text: qsTr("Insurances") + SettingsModel.translationTrigger } CategoryCheckbox { @@ -90,7 +101,8 @@ SectionPage { category: "other" imageSource: Category.imageSource("other") - text: qsTr("Other services") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + text: qsTr("Other services") + SettingsModel.translationTrigger } } } @@ -99,7 +111,7 @@ SectionPage { height: baseItem.separatorHeight width: parent.width - color: Constants.grey_border + color: Style.color.border } Rectangle { @@ -108,25 +120,23 @@ SectionPage { height: baseItem.height - (baseItem.headerHeight + baseItem.separatorHeight) width: parent.width anchors.horizontalCenter: parent.horizontalCenter - color: Constants.background_color + color: Style.color.background - Text { + GText { id: noResultsText - color: Constants.secondary_text anchors.centerIn: mainPane - text: qsTr("No match found") + settingsModel.translationTrigger + //: LABEL ANDROID_TABLET + text: qsTr("No match found") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size visible: !flickable.visible } - Flickable { + GFlickable { id: flickable anchors.fill: mainPane clip: true - flickableDirection: Flickable.VerticalFlick visible: grid.hasResults contentHeight: grid.height @@ -138,7 +148,7 @@ SectionPage { Grid { id: grid - columns: Math.floor((parent.width - Constants.component_spacing) / (Utils.dp(196) + Constants.component_spacing)) + columns: Math.floor((parent.width - Constants.component_spacing) / (196 + Constants.component_spacing)) padding: Constants.component_spacing spacing: Constants.component_spacing width: parent.width @@ -155,8 +165,8 @@ SectionPage { ProviderCard { width: grid.cardWidth headerHeight: width / 1.80 - textHeight: Utils.dp(64) - footerHeight: Utils.dp(30) + textHeight: 64 + footerHeight: 30 pushFunction: baseItem.pushProviderDetails providerModelItem: baseItem.wasVisible ? model : undefined } @@ -166,8 +176,8 @@ SectionPage { id: additionalResults width: grid.cardWidth headerHeight: width / 1.80 - textHeight: Utils.dp(64) - footerHeight: Utils.dp(30) + textHeight: 64 + footerHeight: 30 } } } diff --git a/resources/qml/Governikus/ProviderView/+mobile/+ios/+phone/ProviderView.qml b/resources/qml/Governikus/ProviderView/+mobile/+ios/+phone/ProviderView.qml index d234be9..5ae5423 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+ios/+phone/ProviderView.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/+ios/+phone/ProviderView.qml @@ -1,108 +1,16 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 -import Governikus.Provider 1.0 -import Governikus.TitleBar 1.0 import Governikus.View 1.0 import Governikus.Type.ProviderCategoryFilterModel 1.0 -SectionPage { - id: baseItem - - readonly property var category: ProviderCategoryFilterModel.categories.length === 0 ? "" : ProviderCategoryFilterModel.categories[0] - - Component.onCompleted: ProviderCategoryFilterModel.sortByCategoryFirst(true) - - onCategoryChanged: { - ProviderCategoryFilterModel.sortByCategoryFirst(category === "") - } - - leftTitleBarAction: TitleBarAction { - state: category !== "" ? "back" : "" - onClicked: { - if (state === "back") { - ProviderCategoryFilterModel.setCategorySelection("") - } - } - } - - headerTitleBarAction: TitleBarAction { - text: Category.displayString(category) + settingsModel.translationTrigger - font.bold: true - } - +BaseProviderView { subTitleBarAction: SearchBar { width: baseItem.width onSearchTextChanged: ProviderCategoryFilterModel.searchString = searchText } - - titleBarColor: Category.displayColor(category) - - ProviderDetailView { - id: providerDetailView - visible: false - } - - content: Column { - width: baseItem.width - - Rectangle { - height: Utils.dp(200) - width: parent.width - color: Constants.background_color - visible: ProviderCategoryFilterModel.rowCount === 0 && !additionalResults.visible - - Text { - color: Constants.secondary_text - anchors.centerIn: parent - text: qsTr("No match found") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - } - } - - ProviderSectionDelegate { - id: allSection - sectionName: "all" - visible: ProviderCategoryFilterModel.searchString === "" && ProviderCategoryFilterModel.categories.length === 0 - height: visible ? Constants.provider_section_height : 0 - } - - ListView { - id: providerListMain - height: childrenRect.height - width: baseItem.width - interactive: false - visible: category === "" - - model: ProviderCategoryFilterModel - - delegate: ProviderViewDelegate { - height: visible ? Constants.provider_section_height : 0 - visible: ProviderCategoryFilterModel.searchString !== "" - } - - section.property: "providerCategory" - section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart - section.delegate: ProviderSectionDelegate { - sectionName: section - } - } - - ListView { - id: providerListSection - height: childrenRect.height - width: baseItem.width - interactive: false - visible: !providerListMain.visible - - model: ProviderCategoryFilterModel - - delegate: ProviderViewDelegate {} - } - - AdditionalResultsItem { - id: additionalResults - width: parent.width - } - } } diff --git a/resources/qml/Governikus/ProviderView/+mobile/+ios/+tablet/ProviderView.qml b/resources/qml/Governikus/ProviderView/+mobile/+ios/+tablet/ProviderView.qml index 4c36a1f..7993d9d 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+ios/+tablet/ProviderView.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/+ios/+tablet/ProviderView.qml @@ -1,15 +1,24 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.HistoryModel 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ProviderCategoryFilterModel 1.0 SectionPage { id: baseItem - headerTitleBarAction: TitleBarAction { text: qsTr("Provider") + settingsModel.translationTrigger; font.bold: true } + //: LABEL IOS_TABLET + title: qsTr("Provider") + SettingsModel.translationTrigger + subTitleBarAction: SearchBar { width: baseItem.width onSearchTextChanged: ProviderCategoryFilterModel.searchString = searchText @@ -23,8 +32,8 @@ SectionPage { readonly property var category: ProviderCategoryFilterModel.categories.length === 0 ? "" : ProviderCategoryFilterModel.categories[0] - readonly property int headerHeight: Utils.dp(54) - readonly property int separatorHeight: Utils.dp(2) + readonly property int headerHeight: 54 + readonly property int separatorHeight: 2 ProviderDetailView { id: providerDetailView @@ -32,7 +41,7 @@ SectionPage { } function pushProviderDetails(pModel) { - historyModel.nameFilter.setProviderAddress(pModel.providerAddress) + HistoryModel.nameFilter.setProviderAddress(pModel.providerAddress) providerDetailView.providerModelItem = pModel firePush(providerDetailView) } @@ -47,7 +56,7 @@ SectionPage { height: baseItem.headerHeight width: parent.width - color: "white" + color: Constants.white Row { id: checkBoxesItem @@ -56,8 +65,8 @@ SectionPage { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - padding: Utils.dp(30) - spacing: Utils.dp(30) + padding: 30 + spacing: 30 transformOrigin: Item.Center scale: Math.min(parent.width / width, 1) @@ -67,7 +76,8 @@ SectionPage { category: "citizen" imageSource: Category.imageSource("citizen") - text: qsTr("Citizen services") + settingsModel.translationTrigger + //: LABEL IOS_TABLET + text: qsTr("Citizen services") + SettingsModel.translationTrigger } CategoryCheckbox { @@ -75,7 +85,8 @@ SectionPage { category: "finance" imageSource: Category.imageSource("finance") - text: qsTr("Financials") + settingsModel.translationTrigger + //: LABEL IOS_TABLET + text: qsTr("Financials") + SettingsModel.translationTrigger } CategoryCheckbox { @@ -83,7 +94,8 @@ SectionPage { category: "insurance" imageSource: Category.imageSource("insurance") - text: qsTr("Insurances") + settingsModel.translationTrigger + //: LABEL IOS_TABLET + text: qsTr("Insurances") + SettingsModel.translationTrigger } CategoryCheckbox { @@ -91,7 +103,8 @@ SectionPage { category: "other" imageSource: Category.imageSource("other") - text: qsTr("Other services") + settingsModel.translationTrigger + //: LABEL IOS_TABLET + text: qsTr("Other services") + SettingsModel.translationTrigger } } } @@ -100,7 +113,7 @@ SectionPage { height: baseItem.separatorHeight width: parent.width - color: Constants.grey_border + color: Style.color.border } Rectangle { @@ -109,25 +122,23 @@ SectionPage { height: baseItem.height - (baseItem.headerHeight + baseItem.separatorHeight) width: parent.width anchors.horizontalCenter: parent.horizontalCenter - color: Constants.background_color + color: Style.color.background - Text { + GText { id: noResultsText - color: Constants.secondary_text anchors.centerIn: mainPane - text: qsTr("No match found") + settingsModel.translationTrigger + //: LABEL IOS_TABLET Der in das Suchfeld eingegebene String erzielte kein Ergebnis + text: qsTr("No match found") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size visible: !flickable.visible } - Flickable { + GFlickable { id: flickable anchors.fill: mainPane clip: true - flickableDirection: Flickable.VerticalFlick visible: grid.hasResults contentHeight: grid.height @@ -139,7 +150,7 @@ SectionPage { Grid { id: grid - columns: Math.floor((parent.width - Constants.component_spacing) / (Utils.dp(196) + Constants.component_spacing)) + columns: Math.floor((parent.width - Constants.component_spacing) / (196 + Constants.component_spacing)) padding: Constants.component_spacing spacing: Constants.component_spacing width: parent.width @@ -155,9 +166,13 @@ SectionPage { ProviderCard { width: grid.cardWidth + + Accessible.onScrollDownAction: Utils.scrollPageDown(flickable) + Accessible.onScrollUpAction: Utils.scrollPageUp(flickable) + headerHeight: width / 1.80 - textHeight: Utils.dp(64) - footerHeight: Utils.dp(30) + textHeight: 64 + footerHeight: 30 pushFunction: baseItem.pushProviderDetails providerModelItem: baseItem.wasVisible ? model : undefined } @@ -167,8 +182,8 @@ SectionPage { id: additionalResults width: grid.cardWidth headerHeight: width / 1.80 - textHeight: Utils.dp(64) - footerHeight: Utils.dp(30) + textHeight: 64 + footerHeight: 30 } } } diff --git a/resources/qml/Governikus/ProviderView/+mobile/+phone/AdditionalResultsItem.qml b/resources/qml/Governikus/ProviderView/+mobile/+phone/AdditionalResultsItem.qml index 9472f3a..62c851c 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+phone/AdditionalResultsItem.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/+phone/AdditionalResultsItem.qml @@ -1,60 +1,27 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.2 import Governikus.Global 1.0 import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ProviderCategoryFilterModel 1.0 -Rectangle { - id: baseItem - height: Constants.provider_section_height - +ListItem { property int totalHits: ProviderCategoryFilterModel.additionalResultCount visible: totalHits > 0 && ProviderCategoryFilterModel.categories.length > 0 && ProviderCategoryFilterModel.categories.indexOf("all") === -1 - Item { - anchors.fill: parent - anchors.topMargin: Utils.dp(5) - anchors.bottomMargin: Utils.dp(5) + Accessible.name: qsTr("%1 additional results in other categories").arg(totalHits) + SettingsModel.translationTrigger + Accessible.description: qsTr("Click to remove category filter and show additional results.") + SettingsModel.translationTrigger - Image { - id: allImage - source: Category.imageSource("all") - asynchronous: true - height: parent.height - width: parent.width * 0.15 - fillMode: Image.PreserveAspectFit - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: ProviderStyle.leftIconMargin - } + icon: Category.imageSource("all") + text: qsTr("Additional results:") + ' ' + totalHits + SettingsModel.translationTrigger + showRightArrow: false + showSeparator: false - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: allImage.right - anchors.leftMargin: ProviderStyle.leftProviderListMargin - color: ProviderStyle.categoryColor - font.pixelSize: ProviderStyle.categoryFontPixelSize - font.bold: ProviderStyle.categoryFontBold - elide: Text.ElideRight - text: '' + qsTr("Additional results:") + " " + baseItem.totalHits + '' + settingsModel.translationTrigger - } - - Text { - anchors.right: parent.right - anchors.rightMargin: Utils.dp(5) - anchors.verticalCenter: parent.verticalCenter - - text: ">" - color: Constants.grey - font.pixelSize: Constants.normal_font_size - visible: ProviderStyle.showCategoryRightArrow - } - - MouseArea { - anchors.fill: parent - onClicked: ProviderCategoryFilterModel.setCategorySelection("") - } - } + onClicked: ProviderCategoryFilterModel.setCategorySelection("") } diff --git a/resources/qml/Governikus/ProviderView/+mobile/+phone/BaseProviderView.qml b/resources/qml/Governikus/ProviderView/+mobile/+phone/BaseProviderView.qml new file mode 100644 index 0000000..ba9482b --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+phone/BaseProviderView.qml @@ -0,0 +1,122 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Provider 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + + +SectionPage { + id: baseItem + + readonly property var category: ProviderCategoryFilterModel.categories.length === 0 ? "" : ProviderCategoryFilterModel.categories[0] + + title: Category.displayString(category) + SettingsModel.translationTrigger + titleBarColor: Category.displayColor(category) + + navigationAction: NavigationAction { + state: category !== "" ? "back" : "" + onClicked: { + if (state === "back") { + ProviderCategoryFilterModel.setCategorySelection("") + } + } + } + + Component.onCompleted: ProviderCategoryFilterModel.sortByCategoryFirst(true) + + onCategoryChanged: { + ProviderCategoryFilterModel.sortByCategoryFirst(category === "") + highlightScrollbar() + } + + Component { + id: providerDetailView + + ProviderDetailView {} + } + + content: Column { + width: baseItem.width + + Rectangle { + visible: ProviderCategoryFilterModel.rowCount === 0 && !additionalResults.visible + height: 200 + width: parent.width + + color: Style.color.background + + GText { + anchors.centerIn: parent + //: LABEL IOS_PHONE ANDROID_PHONE Der in das Suchfeld eingegebene String erzielte kein Ergebnis + text: qsTr("No match found") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary + } + } + + ProviderSectionDelegate { + id: allSection + + visible: ProviderCategoryFilterModel.searchString === "" && ProviderCategoryFilterModel.categories.length === 0 + height: visible ? Style.dimens.list_item_height : 0 + + sectionName: "all" + } + + GListView { + id: providerListMain + + visible: category === "" + height: childrenRect.height + width: baseItem.width + + Accessible.role: Accessible.List + + scrollBarEnabled: false + interactive: false + model: ProviderCategoryFilterModel + delegate: ProviderListItemDelegate { + height: visible ? Style.dimens.list_item_height : 0 + visible: ProviderCategoryFilterModel.searchString !== "" + } + + section.property: "providerCategory" + section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart + section.delegate: ProviderSectionDelegate { + sectionName: section + } + } + + GListView { + id: providerListSection + + visible: !providerListMain.visible + height: childrenRect.height + width: baseItem.width + + Accessible.role: Accessible.List + + scrollBarEnabled: false + interactive: false + + model: ProviderCategoryFilterModel + + delegate: ProviderListItemDelegate { + Accessible.onScrollDownAction: baseItem.scrollPageDown() + Accessible.onScrollUpAction: baseItem.scrollPageUp() + } + } + + AdditionalResultsItem { + id: additionalResults + width: parent.width + } + } +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+phone/ProviderListItemDelegate.qml b/resources/qml/Governikus/ProviderView/+mobile/+phone/ProviderListItemDelegate.qml new file mode 100644 index 0000000..0c81c0f --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+phone/ProviderListItemDelegate.qml @@ -0,0 +1,22 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtGraphicalEffects 1.10 + +import Governikus.Global 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + + +ListItem { + property string sectionName + + Accessible.description: qsTr("Open provider details for") + ' ' + display + SettingsModel.translationTrigger + + text: display + footerText: providerAddressDomain + + onClicked: firePushWithProperties(providerDetailView, {providerModelItem: model}) +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+phone/ProviderSectionDelegate.qml b/resources/qml/Governikus/ProviderView/+mobile/+phone/ProviderSectionDelegate.qml new file mode 100644 index 0000000..e99ead5 --- /dev/null +++ b/resources/qml/Governikus/ProviderView/+mobile/+phone/ProviderSectionDelegate.qml @@ -0,0 +1,22 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtGraphicalEffects 1.10 + +import Governikus.Global 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ProviderCategoryFilterModel 1.0 + + +ListItem { + property string sectionName + + Accessible.description: qsTr("Click to set category filter to %1").arg(text) + SettingsModel.translationTrigger + + icon: Category.imageSource(sectionName) + text: Category.displayString(sectionName) + SettingsModel.translationTrigger + + onClicked: ProviderCategoryFilterModel.setCategorySelection(sectionName) +} diff --git a/resources/qml/Governikus/ProviderView/+mobile/+tablet/AdditionalResultsItem.qml b/resources/qml/Governikus/ProviderView/+mobile/+tablet/AdditionalResultsItem.qml index ee45079..1319c77 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+tablet/AdditionalResultsItem.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/+tablet/AdditionalResultsItem.qml @@ -1,9 +1,16 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.2 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ProviderCategoryFilterModel 1.0 + Rectangle { id: baseItem height: column.height @@ -15,6 +22,11 @@ Rectangle { visible: totalHits > 0 + Accessible.role: Accessible.Button + Accessible.name: qsTr("%1 additional results in other categories").arg(totalHits) + SettingsModel.translationTrigger + Accessible.description: qsTr("Click to remove category filter and show additional results.") + SettingsModel.translationTrigger + Accessible.onPressAction: if (Qt.platform.os === "ios") mouseArea.clicked(null) + Column { id: column width: baseItem.width @@ -37,7 +49,7 @@ Rectangle { fillMode: Image.PreserveAspectFit anchors.horizontalCenter: backgroundImage.horizontalCenter anchors.bottom: backgroundImage.bottom - anchors.bottomMargin: Utils.dp(20) + anchors.bottomMargin: 20 } } @@ -46,14 +58,14 @@ Rectangle { height: baseItem.textHeight width: parent.width - Text { - text: '' + qsTr("Additional results:") + " " + baseItem.totalHits + '' + settingsModel.translationTrigger - + GText { anchors.centerIn: parent - font.bold: true - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text + Accessible.ignored: true + + //: LABEL ANDROID_TABLET IOS_TABLET + text: '' + qsTr("Additional results:") + " " + baseItem.totalHits + '' + SettingsModel.translationTrigger + textStyle: Style.text.normal } } @@ -65,7 +77,10 @@ Rectangle { } MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: ProviderCategoryFilterModel.addAdditionalResultCategories() } } diff --git a/resources/qml/Governikus/ProviderView/+mobile/CategoryCheckbox.qml b/resources/qml/Governikus/ProviderView/+mobile/+tablet/CategoryCheckbox.qml similarity index 69% rename from resources/qml/Governikus/ProviderView/+mobile/CategoryCheckbox.qml rename to resources/qml/Governikus/ProviderView/+mobile/+tablet/CategoryCheckbox.qml index 3a0bf23..e5fbf3c 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/CategoryCheckbox.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/+tablet/CategoryCheckbox.qml @@ -1,24 +1,36 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Type.ProviderCategoryFilterModel 1.0 Item { id: baseItem - height: parent.height - width: mainContent.width - anchors.verticalCenter: parent.verticalCenter property string category: "" property alias imageSource: icon.source property alias text: label.text + height: parent.height + width: mainContent.width + anchors.verticalCenter: parent.verticalCenter + + Accessible.role: Accessible.CheckBox + Accessible.name: text + Accessible.checkable: true + Accessible.checked: checkBox.checked + Accessible.onPressAction: if (Qt.platform.os === "ios") mouseArea.clicked(null) + Row { id: mainContent height: parent.height - spacing: Utils.dp(5) + spacing: 5 anchors.verticalCenter: parent.verticalCenter Image { @@ -29,23 +41,32 @@ Item { anchors.verticalCenter: parent.verticalCenter } - Text { + GText { id: label - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size anchors.verticalCenter: parent.verticalCenter + + Accessible.ignored: true + + textStyle: Style.text.normal } GCheckBox { id: checkBox - anchors.verticalCenter: parent.verticalCenter + visible: true + anchors.verticalCenter: parent.verticalCenter + + Accessible.ignored: true + checked: ProviderCategoryFilterModel.categories.indexOf(baseItem.category) !== -1 } } MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: ProviderCategoryFilterModel.updateCategorySelection(category, !checkBox.checked) } } diff --git a/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCard.qml b/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCard.qml index f57644a..a01dbb4 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCard.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCard.qml @@ -1,7 +1,12 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.2 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Provider 1.0 Rectangle { @@ -16,6 +21,10 @@ Rectangle { property alias providerModelItem: provider.modelItem property var pushFunction: function(model) {} + Accessible.role: Accessible.ListItem + Accessible.name: nameRow.providerName + Accessible.onPressAction: if (Qt.platform.os === "ios") mouseArea.clicked(null) + ProviderModelItem { id: provider } @@ -34,7 +43,10 @@ Rectangle { } ProviderCardNameRow { + id: nameRow + height: baseItem.textHeight + providerName: provider.longName !== "" ? provider.longName : provider.shortName headerIcon: provider.icon providerCategory: provider.category @@ -45,26 +57,28 @@ Rectangle { height: baseItem.footerHeight width: parent.width - Text { + GText { text: provider.homepageBase anchors.centerIn: parent + Accessible.ignored: true + leftPadding: Constants.pane_padding rightPadding: Constants.pane_padding elide: Text.ElideRight maximumLineCount: 1 - font.pixelSize: Constants.normal_font_size - color: "white" - - scale: Math.min(1, parent.width / (contentWidth + leftPadding + rightPadding)) + textStyle: Style.text.normal_inverse } } } MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: baseItem.pushFunction(providerModelItem) } } diff --git a/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCardNameRow.qml b/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCardNameRow.qml index f5ddb1d..2f93dc5 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCardNameRow.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/+tablet/ProviderCardNameRow.qml @@ -1,6 +1,11 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 Rectangle { @@ -28,21 +33,19 @@ Rectangle { anchors.leftMargin: parent.padding } - Text { - text: '' + providerName + '' + GText { anchors.left: image.right anchors.leftMargin: parent.padding anchors.top: parent.top anchors.topMargin: parent.height * 0.05 anchors.right: parent.right anchors.rightMargin: parent.padding + + Accessible.ignored: true + + text: '' + providerName + '' elide: Text.ElideRight maximumLineCount: 4 - wrapMode: Text.Wrap - lineHeightMode: Text.FixedHeight - lineHeight: parent.height * 0.90 / 4 - font.bold: true - font.pixelSize: Constants.small_font_size - color: Constants.secondary_text + textStyle: Style.text.hint_secondary } } diff --git a/resources/qml/Governikus/ProviderView/+mobile/DimmableTextButton.qml b/resources/qml/Governikus/ProviderView/+mobile/DimmableTextButton.qml deleted file mode 100644 index 7430099..0000000 --- a/resources/qml/Governikus/ProviderView/+mobile/DimmableTextButton.qml +++ /dev/null @@ -1,33 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 - -import Governikus.Global 1.0 - -Label { - id: label - signal clicked - - color: white - - MouseArea { - id: buttonArea - anchors.fill: parent - onClicked: { - parent.clicked() - } - } - - states: [ - State { - name: "pressed" - when: buttonArea.pressed - - PropertyChanges { - target: label - color: Constants.grey - } - } - ] - transitions: [ - ] -} diff --git a/resources/qml/Governikus/ProviderView/+mobile/ProviderDelegateModel.qml b/resources/qml/Governikus/ProviderView/+mobile/ProviderDelegateModel.qml index 2e44518..49c0330 100644 --- a/resources/qml/Governikus/ProviderView/+mobile/ProviderDelegateModel.qml +++ b/resources/qml/Governikus/ProviderView/+mobile/ProviderDelegateModel.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQml.Models 2.2 import QtQuick 2.10 diff --git a/resources/qml/Governikus/ProviderView/+mobile/ProviderSectionDelegate.qml b/resources/qml/Governikus/ProviderView/+mobile/ProviderSectionDelegate.qml deleted file mode 100644 index d1b769f..0000000 --- a/resources/qml/Governikus/ProviderView/+mobile/ProviderSectionDelegate.qml +++ /dev/null @@ -1,69 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 -import Governikus.Style 1.0 -import Governikus.Type.ProviderCategoryFilterModel 1.0 - -Rectangle { - property string sectionName: "" - - width: parent.width - height: Constants.provider_section_height - clip: true - - Item { - anchors.fill: parent - anchors.topMargin: Utils.dp(5) - anchors.bottomMargin: Utils.dp(5) - - Image { - id: sectionImage - source: Category.imageSource(sectionName) - asynchronous: true - height: parent.height - width: parent.width * 0.15 - fillMode: Image.PreserveAspectFit - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: ProviderStyle.leftIconMargin - } - - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: sectionImage.right - anchors.leftMargin: ProviderStyle.leftProviderListMargin - color: ProviderStyle.categoryColor - font.pixelSize: ProviderStyle.categoryFontPixelSize - font.bold: ProviderStyle.categoryFontBold - elide: Text.ElideRight - text: Category.displayString(sectionName) + settingsModel.translationTrigger - } - - Text { - anchors.right: parent.right - anchors.rightMargin: Utils.dp(5) - anchors.verticalCenter: parent.verticalCenter - - text: ">" - color: Constants.grey - font.pixelSize: Constants.normal_font_size - visible: ProviderStyle.showCategoryRightArrow - } - - MouseArea { - anchors.fill: parent - onClicked: { - ProviderCategoryFilterModel.setCategorySelection(sectionName) - } - } - } - - Rectangle { - width: parent.width * 0.85 - anchors.top: parent.bottom - anchors.topMargin: -height - anchors.right: parent.right - height: 1 - color: Constants.grey - } -} diff --git a/resources/qml/Governikus/ProviderView/qmldir b/resources/qml/Governikus/ProviderView/qmldir index a0c3c96..98fb274 100644 --- a/resources/qml/Governikus/ProviderView/qmldir +++ b/resources/qml/Governikus/ProviderView/qmldir @@ -2,12 +2,13 @@ module ProviderView internal AdditionalResultsItem AdditionalResultsItem.qml internal CategoryCheckbox CategoryCheckbox.qml -internal DimmableTextButton DimmableTextButton.qml internal ProviderCardNameRow ProviderCardNameRow.qml internal ProviderCard ProviderCard.qml internal ProviderContactInfoItem ProviderContactInfoItem.qml internal ProviderDelegateModel ProviderDelegateModel.qml internal ProviderSectionDelegate ProviderSectionDelegate.qml -internal SearchBar SearchBar.qml +internal ProviderListItemDelegate ProviderListItemDelegate.qml +internal ProviderOverview ProviderOverview.qml +internal BaseProviderView BaseProviderView.qml ProviderView 1.0 ProviderView.qml diff --git a/resources/qml/Governikus/RemoteServiceView/AvailableDevicesListDelegate.qml b/resources/qml/Governikus/RemoteServiceView/AvailableDevicesListDelegate.qml index 9222cf6..d04bab7 100644 --- a/resources/qml/Governikus/RemoteServiceView/AvailableDevicesListDelegate.qml +++ b/resources/qml/Governikus/RemoteServiceView/AvailableDevicesListDelegate.qml @@ -1,40 +1,80 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.1 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + MouseArea { - height: Utils.dp(40) - signal requestPairing(string pDeviceId) - onClicked: { - if (isSupported) requestPairing(deviceId) + height: textColumn.height + separator.height + (Constants.groupbox_spacing / 2) + + Accessible.role: Accessible.ListItem + Accessible.name: qsTr("Device %1 is available for pairing").arg(nameText.text) + SettingsModel.translationTrigger + Accessible.onPressAction: if (Qt.platform.os === "ios" && isSupported) clicked(null) + + onClicked: if (isSupported) requestPairing(deviceId) + + LinkQuality { + id: linkQual + + height: nameText.height + width: height + anchors.verticalCenter: parent.verticalCenter + + percent: linkQualityInPercent } - Text { - id: nameText - color: Constants.secondary_text - width: parent.width - font.pixelSize: Utils.dp(16) - anchors.verticalCenter: parent.verticalCenter - opacity: 0.87 - text: { - settingsModel.translationTrigger + Column { + id: textColumn - if (isSupported) { - return remoteDeviceName; + width: parent.width - linkQual.width - anchors.leftMargin + anchors.left: linkQual.right + anchors.leftMargin: Constants.component_spacing + + spacing: 2 + topPadding: spacing + bottomPadding: spacing + + GText { + id: nameText + + width: parent.width + + text: { + SettingsModel.translationTrigger + + if (isSupported) { + return remoteDeviceName; + } + return remoteDeviceName + " (" + remoteDeviceStatus + ")" } - return remoteDeviceName + " (" + qsTr("Unsupported") + ")" + textStyle: Style.text.normal_secondary + } + + GText { + id: dateText + + width: parent.width + + //: LABEL ANDROID IOS + text: (linkQualityInPercent >= 50 ? qsTr("Great quality") : qsTr("Bad quality")) + SettingsModel.translationTrigger + textStyle: Style.text.hint_secondary } } - Rectangle { + GSeparator { + id: separator + width: parent.width - height: Utils.dp(1) - color: "black" - opacity: 0.1 anchors.bottom: parent.bottom + color: Style.color.border_dark } } diff --git a/resources/qml/Governikus/RemoteServiceView/KnownDevicesListDelegate.qml b/resources/qml/Governikus/RemoteServiceView/KnownDevicesListDelegate.qml index 4f47b70..a31592e 100644 --- a/resources/qml/Governikus/RemoteServiceView/KnownDevicesListDelegate.qml +++ b/resources/qml/Governikus/RemoteServiceView/KnownDevicesListDelegate.qml @@ -1,74 +1,105 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.1 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.RemoteServiceModel 1.0 + Item { id: root - height: Utils.dp(60) - Item { - id: textItem - height: childrenRect.height - width: parent.width * 0.8 - anchors.verticalCenter: root.verticalCenter + height: textColumn.height + separator.height + (Constants.groupbox_spacing / 2) - Text { + Accessible.role: Accessible.ListItem + Accessible.name: qsTr("Device %1 has status %2").arg(nameText.text).arg(statusText.text) + SettingsModel.translationTrigger + Accessible.onPressAction: if (Qt.platform === "ios") iconClick.clicked(null) + + LinkQuality { + id: linkQual + + height: nameText.height + anchors.verticalCenter: textColumn.verticalCenter + + percent: linkQualityInPercent + inactive: !isNetworkVisible + } + + Column { + id: textColumn + + width: parent.width - linkQual.width - iconClick.width - 2 * anchors.leftMargin + anchors.left: linkQual.right + anchors.right: deleteIcon.left + anchors.margins: Constants.component_spacing + + spacing: 2 + topPadding: spacing + bottomPadding: spacing + + GText { id: nameText - color: Constants.secondary_text - font.pixelSize: Utils.dp(16) - opacity: 0.87 + + width: parent.width + + text: remoteDeviceName + textStyle: Style.text.normal_secondary + } + + GText { + id: statusText + + width: parent.width + text: { - settingsModel.translationTrigger + SettingsModel.translationTrigger if (!isNetworkVisible) { - return remoteDeviceName; + //: LABEL ANDROID IOS + return qsTr("Not available"); } - if (isSupported) { - return remoteDeviceName + " (" + qsTr("Available") + ")" - } - return remoteDeviceName + " (" + qsTr("Available, but unsupported") + ")" + //: LABEL ANDROID IOS + return remoteDeviceStatus + ", " + (linkQualityInPercent >= 50 ? qsTr("Great quality") : qsTr("Bad quality")) + SettingsModel.translationTrigger } + textStyle: Style.text.hint_secondary } - Text { - id: dateText - color: Constants.secondary_text - anchors.top: nameText.bottom - anchors.topMargin: Utils.dp(2) - font.pixelSize: Utils.dp(14) - opacity: 0.38 - text: qsTr("Last connection:") + " " + lastConnected + settingsModel.translationTrigger + GText { + width: parent.width + + //: LABEL ANDROID IOS + text: qsTr("Last connection:") + " " + lastConnected + SettingsModel.translationTrigger + textStyle: Style.text.hint_secondary } } - MouseArea { - id: iconClick - width: Utils.dp(44) - height: width + Image { + id: deleteIcon + sourceSize.width: Style.dimens.small_icon_size anchors.right: root.right anchors.verticalCenter: root.verticalCenter + source: "qrc:///images/iOS/search_cancel.svg" - Image { - id: icon - width: Utils.dp(22) - height: width - anchors.centerIn: parent - source: "qrc:///images/iOS/search_cancel.svg" - } + MouseArea { + id: iconClick - onClicked: { - RemoteServiceModel.forgetDevice(deviceId) + anchors.fill: parent + + onClicked: RemoteServiceModel.forgetDevice(deviceId) } } - Rectangle { + GSeparator { + id: separator + width: parent.width - height: Utils.dp(1) - color: "black" - opacity: 0.1 anchors.bottom: root.bottom + color: Style.color.border_dark } } diff --git a/resources/qml/Governikus/RemoteServiceView/LinkQuality.qml b/resources/qml/Governikus/RemoteServiceView/LinkQuality.qml new file mode 100644 index 0000000..5911714 --- /dev/null +++ b/resources/qml/Governikus/RemoteServiceView/LinkQuality.qml @@ -0,0 +1,35 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Style 1.0 + +Image { + id: img + + property var percent + property bool inactive: false + + fillMode: Image.PreserveAspectFit + sourceSize.width: Style.dimens.small_icon_size + source: { + if (inactive) { + return "qrc:///images/icon_remote_inactive.svg" + } + if (percent >= 80) { + return "qrc:///images/icon_remote_100.svg" + } + if (percent >= 60) { + return "qrc:///images/icon_remote_75.svg" + } + if (percent >= 40) { + return "qrc:///images/icon_remote_50.svg" + } + if (percent >= 20) { + return "qrc:///images/icon_remote_25.svg" + } + return "qrc:///images/icon_remote_0.svg" + } +} diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceController.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceController.qml index 52ac682..5a41b3c 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServiceController.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceController.qml @@ -1,10 +1,15 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Type.RemoteServiceModel 1.0 -import Governikus.EnterPinView 1.0 +import Governikus.EnterPasswordView 1.0 import Governikus.TitleBar 1.0 import Governikus.Type.ReaderPlugIn 1.0 import Governikus.Type.NumberModel 1.0 +import Governikus.Type.ApplicationModel 1.0 Item { id: controller @@ -13,36 +18,20 @@ Item { target: RemoteServiceModel onFireCurrentStateChanged: { switch (RemoteServiceModel.currentState) { - case "": + case "Initial": break case "StateStartRemoteService": navBar.lockedAndHidden = true - if (Qt.platform.os === "android") { - RemoteServiceModel.readerPlugInType = ReaderPlugIn.NFC; - } else { - RemoteServiceModel.readerPlugInType = ReaderPlugIn.PCSC; - } setWorkflowStateAndContinue("startRemoteService") break case "StateProcessRemoteMessages": - firePopAll() RemoteServiceModel.continueWorkflow() break case "StateEnterPacePasswordRemote": - if (RemoteServiceModel.isBasicReader) { - enterPinView.state = "INITIAL" - setWorkflowStateAndRequestInput("establishPaceChannelRemote", RemoteServiceModel.getPacePasswordId()) - } else { - RemoteServiceModel.continueWorkflow() - } + setWorkflowStateAndRequestInput("establishPaceChannelRemote", RemoteServiceModel.getPasswordType()) break case "StateEnterNewPacePinRemote": - if (RemoteServiceModel.isBasicReader) { - enterPinView.state = "INITIAL" - setWorkflowStateAndRequestInput("changePinRemote", "PIN_NEW") - } else { - RemoteServiceModel.continueWorkflow() - } + setWorkflowStateAndRequestInput("changePinRemote", "PIN_NEW") break case "FinalState": RemoteServiceModel.continueWorkflow() @@ -65,18 +54,21 @@ Item { function setWorkflowStateAndRequestInput(pState, pEnterPinState) { setWorkflowState(pState) - if (RemoteServiceModel.pinPadModeOn()) { + if (RemoteServiceModel.isBasicReader && RemoteServiceModel.pinPadModeOn()) { enterPinView.state = pEnterPinState firePush(enterPinView) + ApplicationModel.nfcRunning = false + } else { + RemoteServiceModel.continueWorkflow() } } - EnterPinView { + EnterPasswordView { id: enterPinView visible: false enableTransportPinLink: RemoteServiceModel.isSaCPinChangeWorkflow - leftTitleBarAction: TitleBarAction { + navigationAction: NavigationAction { state: "cancel" onClicked: { firePop() @@ -84,9 +76,10 @@ Item { } } - onPinEntered: { + onPasswordEntered: { firePop() RemoteServiceModel.continueWorkflow() + ApplicationModel.nfcRunning = true } onChangePinLength: NumberModel.requestTransportPin = !NumberModel.requestTransportPin diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServicePairingPopup.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServicePairingPopup.qml index 00bf600..cb4fe71 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServicePairingPopup.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServicePairingPopup.qml @@ -1,79 +1,43 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.RemoteServiceModel 1.0 -Popup { - property bool requestInput: false +ConfirmationPopup { + id: popup + property alias pin: name.text property var deviceId - id: popup - modal: true - focus: true - closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape - width: Utils.dp(250) - height: contentColumn.height + 2 * Constants.pane_padding - padding: Constants.pane_padding + style: ConfirmationPopup.PopupStyle.NoButtons + //: INFO ANDROID IOS Header of the pairing dialog when using the smartphone as card reader + title: qsTr("Pairing code") + SettingsModel.translationTrigger + //: INFO ANDROID IOS Main text of the pairing dialog when using the smartphone as card reader. + text: qsTr("Enter this code on your other device to use this device as a card reader") + SettingsModel.translationTrigger - Connections { - target: RemoteServiceModel - onFireEnvironmentChanged: close() - } + TextField { + id: name - Column { - id: contentColumn width: parent.width - spacing: Constants.pane_spacing - Text { - id: header - color: Constants.secondary_text - text: qsTr("Pairing code") + settingsModel.translationTrigger - font.pixelSize: Constants.header_font_size - font.bold: true - } + Accessible.role: Accessible.StaticText + Accessible.name: displayText + Accessible.description: qsTr("Click to close dialog") + SettingsModel.translationTrigger + Accessible.onPressAction: if (Qt.platform.os === "ios") popup.close() - Text { - id: info - color: Constants.secondary_text - width: parent.width - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - text: ( requestInput - ? qsTr("Enter the pairing code shown on your other device to use it as a card reader") - : qsTr("Enter this code on your other device to use this device as a card reader") - ) + settingsModel.translationTrigger - } - - TextField { - id: name - focus: true - width: parent.width - horizontalAlignment: Text.AlignHCenter - font.letterSpacing: Utils.dp(5) - font.pixelSize: Utils.dp(50) - font.bold: true - readOnly: !requestInput - inputMethodHints: Qt.ImhDigitsOnly - validator: RegExpValidator { regExp: /\d\d\d\d/ } - onAccepted: { - RemoteServiceModel.connectToRememberedServer(name.getText(0,4)) - close() - } - } - - GButton { - text: qsTr("Start pairing") + settingsModel.translationTrigger - width: parent.width - visible: requestInput - - onClicked: { - RemoteServiceModel.connectToRememberedServer(name.getText(0,4)) - close() - } - } + focus: true + horizontalAlignment: Text.AlignHCenter + font.letterSpacing: 5 + font.pixelSize: 50 + font.bold: true + readOnly: true } } diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceSettings.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceSettings.qml index 062000e..0071961 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServiceSettings.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceSettings.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.1 @@ -5,11 +9,14 @@ import QtQuick.Layouts 1.1 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: rootPage - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Configure remote service") + settingsModel.translationTrigger; font.bold: true } + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + //: LABEL ANDROID IOS + title: qsTr("Configure remote service") + SettingsModel.translationTrigger content: RemoteServiceViewRemote { width: rootPage.width diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceView.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceView.qml index 089da46..f0cfaf9 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServiceView.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceView.qml @@ -1,28 +1,38 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.RemoteServiceModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: Constants.is_layout_ios ? iosTitleBarAction : androidTitleBarAction - headerTitleBarAction: TitleBarAction { - text: sectionSwitch.selectedSection === "LOCAL" ? qsTr("Configure local settings") + settingsModel.translationTrigger - : sectionSwitch.selectedSection === "REMOTE" ? qsTr("Pair remote devices")+ settingsModel.translationTrigger - : qsTr("Remote service") + settingsModel.translationTrigger - font.bold: true } - property var iosTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - property var androidTitleBarAction: TitleBarAction { + navigationAction: NavigationAction { state: RemoteServiceModel.running ? "cancel" : "" - onClicked: RemoteServiceModel.running = !RemoteServiceModel.running + onClicked: RemoteServiceModel.running = false } + //: LABEL ANDROID IOS + title: sectionSwitch.selectedSection === "LOCAL" ? qsTr("Configure local settings") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS + : sectionSwitch.selectedSection === "REMOTE" ? qsTr("Pair remote devices")+ SettingsModel.translationTrigger + //: LABEL ANDROID IOS + : qsTr("Remote service") + SettingsModel.translationTrigger + Connections { target: RemoteServiceModel - onFirePairingFailed: qmlExtension.showFeedback(qsTr("Pairing failed. Please try again to activate pairing on your other device and enter the shown pairing code.")) + //: ERROR ANDROID IOS An error occurred while pairing the device. + onFirePairingFailed: ApplicationModel.showFeedback(qsTr("Pairing failed. Please try again to activate pairing on your other device and enter the shown pairing code.")) } onVisibleChanged: { @@ -34,44 +44,47 @@ SectionPage { } } - content: Column { - width: baseItem.width + content: ColumnLayout { + height: baseItem.height - RemoteServiceViewLocal - { - width: parent.width - visible: sectionSwitch.selectedSection === "LOCAL" + RemoteServiceViewStartStop { + visible: sectionSwitch.selectedSection == SectionSwitch.Section.STARTSTOP + width: baseItem.width + Layout.fillHeight: true + } + GFlickable { + id: flickable + + visible: sectionSwitch.selectedSection == SectionSwitch.Section.REMOTE + width: baseItem.width + Layout.fillHeight: true + + contentHeight: remoteSettingsView.height + RemoteServiceViewRemote { + id: remoteSettingsView + width: baseItem.width + parentSectionPage: baseItem + } + } + RemoteServiceViewLocal { + visible: sectionSwitch.selectedSection == SectionSwitch.Section.LOCAL + width: baseItem.width + Layout.fillHeight: true } - RemoteServiceViewRemote - { - id: remoteSettingsView - width: parent.width - visible: sectionSwitch.selectedSection === "REMOTE" - parentSectionPage: baseItem - } + SectionSwitch { + id: sectionSwitch - RemoteServiceViewStartStop - { - width: parent.width - height: baseItem.height - visible: Constants.is_layout_android && sectionSwitch.selectedSection === "STARTSTOP" - } - } + visible: !RemoteServiceModel.running + Layout.fillWidth: true - SectionSwitch { - id: sectionSwitch - visible: !RemoteServiceModel.running - showStartStopButton: Constants.is_layout_android - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - onSelectedSectionChanged: { - if (selectedSection === "REMOTE" && !RemoteServiceModel.detectRemoteDevices){ - RemoteServiceModel.detectRemoteDevices = true - } else { - RemoteServiceModel.detectRemoteDevices = false + onSelectedSectionChanged: { + if (selectedSection == SectionSwitch.Section.REMOTE) { + RemoteServiceModel.detectRemoteDevices = true + flickable.highlightScrollbar() + } else { + RemoteServiceModel.detectRemoteDevices = false + } } } } diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewLocal.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewLocal.qml index 0ea5962..dc56f1d 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewLocal.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewLocal.qml @@ -1,8 +1,15 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.RemoteServiceModel 1.0 + Item { id: baseItem height: mainColumn.height @@ -15,39 +22,47 @@ Item { readonly property int usableWidth: width - 2 * padding - Text { + GText { id: errorMsg - width: parent.usableWidth - text: "" - color: Constants.red - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap - verticalAlignment: Text.AlignVCenter + visible: text !== "" + width: parent.usableWidth + + Accessible.role: Accessible.StaticText + Accessible.name: text + + verticalAlignment: Text.AlignVCenter + text: "" + textStyle: Style.text.normal_warning } Column { id: nameContainer width: parent.usableWidth - Text { - text: qsTr("Device name") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - font.bold: true - color: Constants.blue + GText { + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: LABEL ANDROID IOS + text: qsTr("Device name") + SettingsModel.translationTrigger + textStyle: Style.text.normal_accent } - Text { - color: Constants.secondary_text - text: qsTr("Set device name:") + settingsModel.translationTrigger + GText { width: parent.width - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap + + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: LABEL ANDROID IOS + text: qsTr("Set device name:") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } Item { id: spacing - height: Utils.dp(5) + height: 5 width: height } @@ -55,47 +70,56 @@ Item { id: serverName width: parent.width onAccepted: { - settingsModel.serverName = text - text = settingsModel.serverName + SettingsModel.serverName = text + text = SettingsModel.serverName nameContainer.forceActiveFocus(Qt.MouseFocusReason) } onVisibleChanged: { nameContainer.forceActiveFocus(Qt.MouseFocusReason) - if (visible) text = settingsModel.serverName + if (visible) text = SettingsModel.serverName } } } Item { id: pinPadModeContainer - visible: Qt.platform.os !== "ios" + width: parent.usableWidth height: Math.max(pinPadModeText.height, pinPadModeSwitch.height) Item { id: pinPadModeText + height: nameText.height + dateText.height anchors.left: pinPadModeContainer.left anchors.right: pinPadModeSwitch.left anchors.rightMargin: Constants.component_spacing anchors.verticalCenter: pinPadModeContainer.verticalCenter - Text { + GText { id: nameText - anchors.bottomMargin: Utils.dp(2) - font.pixelSize: Constants.normal_font_size - color: Constants.blue - font.bold: true - text: qsTr("PIN pad mode") + settingsModel.translationTrigger + + anchors.bottomMargin: 2 + + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: LABEL ANDROID IOS + text: qsTr("PIN pad mode") + SettingsModel.translationTrigger + textStyle: Style.text.normal_accent } - Text { + GText { id: dateText + width: parent.width anchors.top: nameText.bottom - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - text: qsTr("Enter PIN on this device") + settingsModel.translationTrigger - wrapMode: Text.WordWrap + + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: LABEL ANDROID IOS + text: qsTr("Enter PIN on this device") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } } @@ -103,8 +127,8 @@ Item { id: pinPadModeSwitch anchors.right: pinPadModeContainer.right anchors.verticalCenter: pinPadModeContainer.verticalCenter - initialState: settingsModel.pinPadMode - onSwitched: settingsModel.pinPadMode = pinPadModeSwitch.isOn + initialState: SettingsModel.pinPadMode + onSwitched: SettingsModel.pinPadMode = pinPadModeSwitch.isOn } } } diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewRemote.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewRemote.qml index 451ffce..05310b8 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewRemote.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewRemote.qml @@ -1,11 +1,18 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 -import Governikus.EnterPinView 1.0 +import Governikus.EnterPasswordView 1.0 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.RemoteServiceModel 1.0 + Item { id: baseItem height: mainColumn.height @@ -24,26 +31,33 @@ Item { Column { width: parent.usableWidth - Text { - text: qsTr("Paired devices") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - font.bold: true - color: Constants.blue + GText { + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: LABEL ANDROID IOS + text: qsTr("Paired devices") + SettingsModel.translationTrigger + textStyle: Style.text.normal_accent + bottomPadding: Constants.groupbox_spacing } - Text { - color: Constants.secondary_text - text: qsTr("No device is paired.") + settingsModel.translationTrigger + GText { width: parent.width visible: !knownDeviceList.visible - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap + + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: LABEL ANDROID IOS + text: qsTr("No device is paired.") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } ListView { id: knownDeviceList width: parent.width height: childrenRect.height + spacing: Constants.groupbox_spacing / 2 model: RemoteServiceModel.knownDevices delegate: KnownDevicesListDelegate { width: knownDeviceList.width @@ -56,20 +70,26 @@ Item { Column { width: parent.usableWidth - Text { - text: qsTr("Available devices") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - font.bold: true - color: Constants.blue + GText { + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: LABEL ANDROID IOS + text: qsTr("Available devices") + SettingsModel.translationTrigger + textStyle: Style.text.normal_accent + bottomPadding: Constants.groupbox_spacing } - Text { - color: Constants.secondary_text - text: qsTr("No new remote reader was found on your network. Make sure that the remote reader functionality in AusweisApp2 on your other device is activated and that your devices are connected to the same network.") + settingsModel.translationTrigger + GText { width: parent.width visible: !searchDeviceList.visible - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap + + Accessible.role: Accessible.StaticText + Accessible.name: text + + //: INFO ANDROID IOS No remote reader was found on the network, both devices need to be connected to the same wifi network. + text: qsTr("No new remote reader was found on your network. Make sure that the remote reader functionality in AusweisApp2 on your other device is activated and that your devices are connected to the same network.") + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } ListView { @@ -77,6 +97,7 @@ Item { width: parent.width height: childrenRect.height model: RemoteServiceModel.availableRemoteDevices + spacing: Constants.groupbox_spacing / 2 delegate: AvailableDevicesListDelegate { width: searchDeviceList.width onRequestPairing: { @@ -92,52 +113,30 @@ Item { } } - Popup { + ConfirmationPopup { id: informationPairingPopup - x: (parentSectionPage.width - width) / 2 - y: (parentSectionPage.height - height) / 2 - width: Utils.dp(250) - height: contentColumn.height + 2 * Constants.pane_padding - modal: true - focus: true - padding: Constants.pane_padding - closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape - Column { - id: contentColumn - width: parent.width - spacing: Constants.pane_spacing + style: ConfirmationPopup.PopupStyle.OkButton + //: INFO ANDROID IOS + title: qsTr("Pairing mode") + SettingsModel.translationTrigger + //: INFO ANDROID IOS Information dialog that requests the user to start the pairing mode on the smarthpone. + text: qsTr("Please start pairing mode first.") + SettingsModel.translationTrigger - Text { - id: info - color: Constants.secondary_text - width: parent.width - wrapMode: Text.WordWrap - font.pixelSize: Constants.normal_font_size - text: qsTr("Please start pairing mode first.") + settingsModel.translationTrigger - } - - GButton { - text: qsTr("OK") + settingsModel.translationTrigger - width: parent.width - - onClicked: { - informationPairingPopup.close() - pinEntryInProgress = true - firePush(enterPinView) - } - } + onConfirmed: { + pinEntryInProgress = true + firePush(enterPinView) } } - EnterPinView { + EnterPasswordView { id: enterPinView state: "REMOTE_PIN" - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: { firePop(); pinEntryInProgress = false } } - headerTitleBarAction: TitleBarAction { text: qsTr("Pairing code") + settingsModel.translationTrigger } + navigationAction: NavigationAction { state: "cancel"; onClicked: { firePop(); pinEntryInProgress = false } } + //: LABEL ANDROID IOS + title: qsTr("Pairing code") + SettingsModel.translationTrigger visible: false - onPinEntered: { + onPasswordEntered: { firePop() pinEntryInProgress = false } diff --git a/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewStartStop.qml b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewStartStop.qml index 2bf8de6..bb90b64 100644 --- a/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewStartStop.qml +++ b/resources/qml/Governikus/RemoteServiceView/RemoteServiceViewStartStop.qml @@ -1,14 +1,21 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 -import Governikus.EnterPinView 1.0 import Governikus.Global 1.0 -import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.RemoteServiceModel 1.0 + Item { id: baseItem - readonly property int maxWidth: Math.min(width - 2 * Constants.component_spacing, Utils.dp(500)) + readonly property int maxWidth: Math.min(width - 2 * Constants.component_spacing, Style.dimens.max_text_width) RemoteServicePairingPopup { id: popup @@ -30,57 +37,65 @@ Item { anchors.top: parent.top anchors.margins: Constants.component_spacing anchors.horizontalCenter: parent.horizontalCenter - height: parent.height * 0.2 width: parent.maxWidth fillMode: Image.PreserveAspectFit } - Text { + GText { id: text - color: Constants.secondary_text + + Accessible.role: Accessible.StaticText + Accessible.name: text.text width: parent.maxWidth anchors.top: image.bottom anchors.margins: Constants.component_spacing anchors.horizontalCenter: parent.horizontalCenter + //: INFO ANDROID IOS The remote service is active. Hint that both devices need to be connected to the same network. text: qsTr("Please start the remote service in order to use your smartphone as a card reader with AusweisApp2." + " Please note: Both your devices have to be connected to the same WiFi.") - + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap + + SettingsModel.translationTrigger + textStyle: Style.text.normal_secondary } GButton { readonly property bool running: RemoteServiceModel.running readonly property bool canEnableNfc: RemoteServiceModel.canEnableNfc + property bool serviceIsStarting: false id: startButton - buttonColor: running ? "red" : "green" + buttonColor: running ? Constants.red : Constants.green anchors.top: text.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.margins: Constants.component_spacing - enabled: canEnableNfc || RemoteServiceModel.runnable || running + enabled: (canEnableNfc || RemoteServiceModel.runnable || running) && !serviceIsStarting onClicked: { if (canEnableNfc) { - qmlExtension.showSettings("android.settings.NFC_SETTINGS") + ApplicationModel.showSettings(ApplicationModel.SETTING_NFC) } else { - var newRunning = !running; - RemoteServiceModel.running = newRunning + if (!running) { + serviceIsStarting = true + } + RemoteServiceModel.running = !running } } text: { - settingsModel.translationTrigger; // Bind this evaluation to the trigger. + SettingsModel.translationTrigger; // Bind this evaluation to the trigger. if (canEnableNfc) { + //: LABEL ANDROID IOS return qsTr("Enable NFC"); } else if (running) { + //: LABEL ANDROID IOS return qsTr("Stop remote service"); } else { + //: LABEL ANDROID IOS return qsTr("Start remote service"); } } onRunningChanged: { + serviceIsStarting = false navBar.lockedAndHidden = running } } @@ -90,7 +105,8 @@ Item { anchors.top: startButton.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.margins: Constants.component_spacing - text: qsTr("Start pairing") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Start pairing") + SettingsModel.translationTrigger opacity: 0 enabled: opacity === 1 onClicked: popup.open() @@ -106,20 +122,21 @@ Item { anchors.margins: Constants.component_spacing anchors.topMargin: Constants.component_spacing * 2 - Text { + GText { id: error - width: text.width + width: text.width anchors.top: parent.top anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter + + Accessible.role: Accessible.StaticText + Accessible.name: error.text + horizontalAlignment: Text.AlignHCenter - font.pixelSize: Utils.dp(16) - font.bold: true - color: "red" - wrapMode: Text.WordWrap visible: !RemoteServiceModel.runnable - text: RemoteServiceModel.errorMessage; + text: RemoteServiceModel.errorMessage + textStyle: Style.text.normal_warning } Item { @@ -127,39 +144,46 @@ Item { anchors.fill: parent opacity: 0 - Text { + GText { id: headText + anchors.top: connectedText.top anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: Constants.header_font_size - font.weight: Font.Bold - color: Constants.blue - text: qsTr("Card access in progress") + settingsModel.translationTrigger; + Accessible.role: Accessible.StaticText + Accessible.name: headText.text + + //: LABEL ANDROID IOS + text: qsTr("Card access in progress") + SettingsModel.translationTrigger; + textStyle: Style.text.header_accent } - Text { + GText { id: subText - color: Constants.secondary_text - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.normal_font_size + anchors.top: headText.bottom - anchors.topMargin: Utils.dp(10) + anchors.topMargin: 10 anchors.horizontalCenter: parent.horizontalCenter width: connectedText.width * 0.8 - wrapMode: Text.WordWrap - text: qsTr("Please pay attention to the display on your other device %1.").arg("\"" + RemoteServiceModel.connectedClientDeviceName + "\"") + settingsModel.translationTrigger; + Accessible.role: Accessible.StaticText + Accessible.name: subText.text + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: RemoteServiceModel.connectionInfo + textStyle: Style.text.normal_secondary } states: [ State { name: "UNCONNECTED"; when: RemoteServiceModel.running && !RemoteServiceModel.connected PropertyChanges { target: connectedText; opacity: 0 } PropertyChanges { target: pairingButton; opacity: 1 } + PropertyChanges { target: ApplicationModel; nfcRunning: false; restoreEntryValues: false } }, State { name: "CONNECTED"; when: RemoteServiceModel.running && RemoteServiceModel.connected PropertyChanges { target: connectedText; opacity: 1 } PropertyChanges { target: pairingButton; opacity: 0 } + PropertyChanges { target: ApplicationModel; nfcRunning: true; restoreEntryValues: false } } ] transitions: [ diff --git a/resources/qml/Governikus/RemoteServiceView/SectionSwitch.qml b/resources/qml/Governikus/RemoteServiceView/SectionSwitch.qml index f74babc..024c7ff 100644 --- a/resources/qml/Governikus/RemoteServiceView/SectionSwitch.qml +++ b/resources/qml/Governikus/RemoteServiceView/SectionSwitch.qml @@ -1,43 +1,56 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TechnologyInfo 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.RemoteServiceModel 1.0 + Rectangle { id: baseItem height: sectionRow.height - color: Constants.background_color + color: Style.color.background - property bool showStartStopButton: true - property string selectedSection: showStartStopButton ? "STARTSTOP" : "REMOTE" + enum Section { + STARTSTOP = 0, + REMOTE = 1, + LOCAL = 2 + } + property int selectedSection: SectionSwitch.Section.STARTSTOP Row { id: sectionRow - spacing: Utils.dp(30) + spacing: 30 anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter TechnologySwitchButton { - visible: showStartStopButton - buttonActive: selectedSection !== "STARTSTOP" - onClicked: selectedSection = "STARTSTOP" + buttonActive: selectedSection !== SectionSwitch.Section.STARTSTOP + onClicked: selectedSection = SectionSwitch.Section.STARTSTOP imageSource: "qrc:///images/icon_remote.svg" - text: qsTr("Service") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Service") + SettingsModel.translationTrigger } TechnologySwitchButton { - buttonActive: selectedSection !== "REMOTE" - onClicked: selectedSection = "REMOTE" + buttonActive: selectedSection !== SectionSwitch.Section.REMOTE + onClicked: selectedSection = SectionSwitch.Section.REMOTE imageSource: "qrc:///images/icon_pair.svg" - text: qsTr("Pairing") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Pairing") + SettingsModel.translationTrigger } TechnologySwitchButton { - buttonActive: selectedSection !== "LOCAL" - onClicked: selectedSection = "LOCAL" + buttonActive: selectedSection !== SectionSwitch.Section.LOCAL + onClicked: selectedSection = SectionSwitch.Section.LOCAL imageSource: "qrc:///images/icon_settings.svg" - text: qsTr("Settings") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Settings") + SettingsModel.translationTrigger } } } diff --git a/resources/qml/Governikus/ResultView/+desktop/ResultView.qml b/resources/qml/Governikus/ResultView/+desktop/ResultView.qml index 2b32f84..b647eee 100644 --- a/resources/qml/Governikus/ResultView/+desktop/ResultView.qml +++ b/resources/qml/Governikus/ResultView/+desktop/ResultView.qml @@ -1,52 +1,107 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 import Governikus.TitleBar 1.0 +import Governikus.Style 1.0 import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: baseItem - property alias text: resultText.text - property bool isError: false + enum Type { + IsSuccess, + IsError, + IsInfo + } - KeyNavigation.tab: button - Accessible.role: Accessible.Grouping - Accessible.name: qsTr("Result view") + settingsModel.translationTrigger - Accessible.description: qsTr("This is the result of an authentication.") + settingsModel.translationTrigger + signal emailButtonPressed() + property alias emailButtonVisible: emailButton.visible + property alias text: resultText.text + property int resultType: Type.IsSuccess + + Accessible.name: qsTr("Result view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the result of an authentication.") + SettingsModel.translationTrigger + Keys.onReturnPressed: button.onClicked() + Keys.onEnterPressed: button.onClicked() + Keys.onEscapePressed: button.onClicked() StatusIcon { - height: ApplicationModel.scaleFactor * 400 + height: Style.dimens.status_icon_large anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.top anchors.verticalCenterOffset: baseItem.height / 4 - source: isError ? "qrc:///images/status_error.svg" : "qrc:///images/status_ok.svg" + + source: { + switch (resultType) { + case ResultView.Type.IsSuccess: + return "qrc:///images/status_ok.svg" + case ResultView.Type.IsInfo: + return "qrc:///images/status_info.svg" + case ResultView.Type.IsError: + return "qrc:///images/status_error.svg" + } + } } - Text { + GText { id: resultText + + visible: text !== "" + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.verticalCenter - width: parent.width - (2 * Constants.pane_padding) - font.pixelSize: Constants.header_font_size + activeFocusOnTab: true + Accessible.role: Accessible.Heading + Accessible.name: resultText.text + horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - wrapMode: Text.WordWrap - color: Constants.white + textStyle: Style.text.title + onLinkActivated: Qt.openUrlExternally(link) + + FocusFrame { + dynamic: false + } } - ContinueButton { - id: button - height: ApplicationModel.scaleFactor * 160 - anchors.verticalCenter: parent.bottom - anchors.verticalCenterOffset: -baseItem.height / 6 - anchors.horizontalCenter: parent.horizontalCenter - onClicked: baseItem.nextView(SectionPage.Views.Main) + GButton { + id: emailButton - KeyNavigation.tab: baseItem.navSuccessor + visible: false + + icon.source: "qrc:///images/provider/mail.svg" + //: LABEL DESKTOP_QML + text: qsTr("Send email") + SettingsModel.translationTrigger + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.top + verticalCenterOffset: baseItem.height * 3 / 4 + } + onClicked: baseItem.emailButtonPressed() + } + + NavigationButton { + id: button + + anchors { + margins: Constants.component_spacing + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + + activeFocusOnTab: true + + buttonType: Qt.ForwardButton + onClicked: baseItem.nextView(SectionPage.Views.Main) } } diff --git a/resources/qml/Governikus/ResultView/+mobile/ResultView.qml b/resources/qml/Governikus/ResultView/+mobile/ResultView.qml index bd65f53..4f221b4 100644 --- a/resources/qml/Governikus/ResultView/+mobile/ResultView.qml +++ b/resources/qml/Governikus/ResultView/+mobile/ResultView.qml @@ -1,13 +1,20 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.LogModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: baseItem.clicked() } + navigationAction: NavigationAction { state: "cancel"; onClicked: baseItem.clicked() } enum Type { IsSuccess, @@ -23,15 +30,15 @@ SectionPage { Rectangle { anchors.fill: parent - color: Constants.background_color + color: Style.color.background } StatusIcon { id: resultIcon - height: Utils.dp(100) + height: 100 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top - anchors.topMargin: Utils.dp(60) + anchors.topMargin: 60 source: { switch (resultType) { case ResultView.Type.IsSuccess: @@ -44,26 +51,17 @@ SectionPage { } } - Text { + GText { id: resultText anchors.horizontalCenter: parent.horizontalCenter anchors.top: resultIcon.bottom anchors.bottom: buttonRow.top width: parent.width - (2 * Constants.pane_padding) - font.pixelSize: Constants.header_font_size horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - wrapMode: Text.WordWrap - color: { - switch (resultType) { - case ResultView.Type.IsSuccess: - case ResultView.Type.IsInfo: - return Constants.blue - case ResultView.Type.IsError: - return Constants.red - } - } + textStyle: resultType !== ResultView.Type.IsError ? Style.text.header_accent : Style.text.header_warning + onLinkActivated: Qt.openUrlExternally(link) } @@ -72,13 +70,14 @@ SectionPage { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter - anchors.bottomMargin: Utils.dp(30) + anchors.bottomMargin: 30 spacing: Constants.component_spacing GButton { id: buttonLeft - text: qsTr("OK") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("OK") + SettingsModel.translationTrigger onClicked: baseItem.clicked() } @@ -86,7 +85,8 @@ SectionPage { id: buttonRight visible: false - text: qsTr("Send log file") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Send log file") + SettingsModel.translationTrigger onClicked: LogModel.mailLog() } } diff --git a/resources/qml/Governikus/SelfAuthenticationView/+desktop/SelfAuthenticationView.qml b/resources/qml/Governikus/SelfAuthenticationView/+desktop/SelfAuthenticationView.qml new file mode 100644 index 0000000..f08ffe8 --- /dev/null +++ b/resources/qml/Governikus/SelfAuthenticationView/+desktop/SelfAuthenticationView.qml @@ -0,0 +1,92 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Type.SelfAuthModel 1.0 +import Governikus.View 1.0 +import QtQuick 2.10 + +SectionPage { + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Identify") + SettingsModel.translationTrigger + } + + Column { + width: Style.dimens.max_text_width + anchors.centerIn: parent + + spacing: Constants.pane_spacing + Item { + height: childrenRect.height + width: parent.width + + Image { + id: useNpa + + width: ApplicationModel.scaleFactor * 400 + anchors.left: parent.left + + fillMode: Image.PreserveAspectFit + source: "qrc:///images/siteWithLogo.png" + mipmap: true + } + + GText { + anchors.verticalCenter: useNpa.verticalCenter + anchors.leftMargin: Constants.component_spacing + anchors.rightMargin: Constants.component_spacing + anchors.left: useNpa.right + anchors.right: parent.right + + textStyle: Style.text.header + + //: LABEL DESKTOP_QML + text: qsTr("You can use your ID card anywhere you see this logo.") + SettingsModel.translationTrigger + } + } + Pane { + id: textPane + + anchors { + left: parent.left + right: parent.right + } + + GText { + id: info + + width: parent.width + + textStyle: Style.text.normal_inverse + //: LABEL DESKTOP_QML + text: qsTr("Use the button 'See my personal data' to display the data stored on your ID card." + + " An Internet connection is required to display the data.") + + "

" + + qsTr("Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed.") + .arg('' + qsTr("data privacy statement") + '') + + "" + + SettingsModel.translationTrigger + + onLinkActivated: Qt.openUrlExternally(link) + } + + GButton { + anchors.right: parent.right + + icon.source: "qrc:///images/npa.svg" + buttonColor: SettingsModel.useSelfauthenticationTestUri ? Constants.red : Style.color.accent + //: LABEL DESKTOP_QML + text: qsTr("See my personal data") + SettingsModel.translationTrigger + onClicked: SelfAuthModel.startWorkflow() + } + } + } + +} diff --git a/resources/qml/Governikus/MainView/+mobile/MainView.qml b/resources/qml/Governikus/SelfAuthenticationView/+mobile/SelfAuthenticationView.qml similarity index 59% rename from resources/qml/Governikus/MainView/+mobile/MainView.qml rename to resources/qml/Governikus/SelfAuthenticationView/+mobile/SelfAuthenticationView.qml index 88c64e4..82db62e 100644 --- a/resources/qml/Governikus/MainView/+mobile/MainView.qml +++ b/resources/qml/Governikus/SelfAuthenticationView/+mobile/SelfAuthenticationView.qml @@ -1,8 +1,15 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.SelfAuthModel 1.0 Item { id: baseItem @@ -18,14 +25,14 @@ Item { case 7: case 8: case 9: - qmlExtension.showFeedback(10-d.testPkiCounter + qsTr(" more presses to toggle test PKI.") + settingsModel.translationTrigger) + ApplicationModel.showFeedback(qsTr("%1 more presses to toggle test PKI.").arg(10 - d.testPkiCounter) + SettingsModel.translationTrigger, true) break; case 10: - settingsModel.useSelfauthenticationTestUri = !settingsModel.useSelfauthenticationTestUri - if(settingsModel.useSelfauthenticationTestUri) { - qmlExtension.showFeedback(qsTr("Test PKI activated.") + settingsModel.translationTrigger) + SettingsModel.useSelfauthenticationTestUri = !SettingsModel.useSelfauthenticationTestUri + if(SettingsModel.useSelfauthenticationTestUri) { + ApplicationModel.showFeedback(qsTr("Test PKI activated.") + SettingsModel.translationTrigger, true) } else { - qmlExtension.showFeedback(qsTr("Test PKI deactivated.") + settingsModel.translationTrigger) + ApplicationModel.showFeedback(qsTr("Test PKI deactivated.") + SettingsModel.translationTrigger, true) } d.testPkiCounter = 0; break; @@ -63,15 +70,13 @@ Item { } } - Text { - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size + GText { anchors.verticalCenter: useNpa.verticalCenter anchors.leftMargin: Constants.component_spacing - wrapMode: Text.WordWrap anchors.left: useNpa.right anchors.right: parent.right - text: qsTr("You can use your ID card anywhere you see this logo.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("You can use your ID card anywhere you see this logo.") + SettingsModel.translationTrigger } } @@ -80,20 +85,28 @@ Item { anchors.leftMargin: Constants.pane_padding anchors.rightMargin: Constants.pane_padding - Text { + GText { id: info - color: Constants.secondary_text - font.pixelSize: Constants.normal_font_size - wrapMode: Text.WordWrap + + property string htmlLink: qsTr("https://www.ausweisapp.bund.de/datenschutz/") + SettingsModel.translationTrigger anchors.left: parent.left anchors.right: parent.right + + Accessible.role: Accessible.Link + Accessible.name: ApplicationModel.stripHtmlTags(text) + Accessible.description: qsTr("Click to open link to data privacy statement in browser: %1").arg(htmlLink) + SettingsModel.translationTrigger + Accessible.onPressAction: if (Qt.platform.os === "ios") Qt.openUrlExternally(htmlLink) + + wrapMode: Text.WordWrap + //: LABEL ANDROID IOS text: qsTr("Use the button 'See my personal data' to display the data stored on your ID card." + " An Internet connection is required to display the data.") + "

" + //: LABEL ANDROID IOS + qsTr("Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed.") - .arg('' + qsTr("data privacy statement") + '') + .arg('' + qsTr("data privacy statement") + '') + "" - + settingsModel.translationTrigger + + SettingsModel.translationTrigger onLinkActivated: Qt.openUrlExternally(link) } } @@ -105,12 +118,13 @@ Item { anchors.bottom: baseItem.bottom anchors.bottomMargin: Constants.pane_padding anchors.horizontalCenter: parent.horizontalCenter - buttonColor: settingsModel.useSelfauthenticationTestUri ? Constants.red : Constants.blue - text: qsTr("See my personal data") + settingsModel.translationTrigger + buttonColor: SettingsModel.useSelfauthenticationTestUri ? Style.color.warning_text : Style.color.accent + //: LABEL ANDROID IOS + text: qsTr("See my personal data") + SettingsModel.translationTrigger enabled: ApplicationModel.currentWorkflow !== "authentication" onClicked: { - selfAuthModel.startWorkflow() + SelfAuthModel.startWorkflow() } } } diff --git a/resources/qml/Governikus/SelfAuthenticationView/qmldir b/resources/qml/Governikus/SelfAuthenticationView/qmldir new file mode 100644 index 0000000..2734387 --- /dev/null +++ b/resources/qml/Governikus/SelfAuthenticationView/qmldir @@ -0,0 +1,3 @@ +module SelfAuthenticationView + +SelfAuthenticationView 1.0 SelfAuthenticationView.qml diff --git a/resources/qml/Governikus/SettingsView/+desktop/CardReaderDelegate.qml b/resources/qml/Governikus/SettingsView/+desktop/CardReaderDelegate.qml new file mode 100644 index 0000000..e6efeba --- /dev/null +++ b/resources/qml/Governikus/SettingsView/+desktop/CardReaderDelegate.qml @@ -0,0 +1,83 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.12 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.View 1.0 + + +Item { + property int iconHeight: ApplicationModel.scaleFactor * 175 + + implicitHeight: rowLayout.implicitHeight + activeFocusOnTab: true + + FocusFrame { + dynamic: false + border.color: Constants.black + } + + RowLayout { + id: rowLayout + + width: parent.width + + spacing: 0 + + Image { + id: readerIcon + + sourceSize.height: iconHeight + + asynchronous: true + source: readerImagePath + fillMode: Image.PreserveAspectFit + } + + ColumnLayout { + id: textColumn + + Layout.fillHeight: true + Layout.alignment: Qt.AlignLeft + spacing: Constants.text_spacing + + GText { + Layout.fillWidth: true + + textStyle: Style.text.header_inverse + text: readerName + } + + GText { + Layout.fillWidth: true + + textStyle: Style.text.normal_inverse + text: readerStatus + } + + GText { + visible: !readerInstalledAndSupported + + Layout.fillWidth: true + + textStyle: Style.text.normal_inverse + text: readerHTMLDescription + } + } + + Image { + id: statusIcon + + sourceSize.height: iconHeight * 0.33 + + fillMode: Image.PreserveAspectFit + asynchronous: true + source: readerInstalledAndSupported ? "qrc:/images/status_ok.svg" : "qrc:/images/status_error.svg" + } + } +} diff --git a/resources/qml/Governikus/SettingsView/+desktop/CardReaderView.qml b/resources/qml/Governikus/SettingsView/+desktop/CardReaderView.qml new file mode 100644 index 0000000..dac5610 --- /dev/null +++ b/resources/qml/Governikus/SettingsView/+desktop/CardReaderView.qml @@ -0,0 +1,104 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.12 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ReaderDriverModel 1.0 +import Governikus.Type.ReaderScanEnabler 1.0 +import Governikus.View 1.0 + + +Column { + id: root + + spacing: Constants.component_spacing + + ReaderScanEnabler { + } + + Column { + visible: readerRepeater.count > 0 + + width: parent.width + + Repeater { + id: readerRepeater + + model: ReaderDriverModel + delegate: CardReaderDelegate { + width: parent.width + + GSeparator { + visible: index < readerRepeater.count - 1 + width: parent.width + anchors.bottom: parent.bottom + } + } + } + } + + GText { + id: placeHolderText + + visible: readerRepeater.count === 0 + + width: parent.width + + activeFocusOnTab: true + + text: ReaderDriverModel.emptyListDescriptionString + verticalAlignment: Text.AlignVCenter + textStyle: Style.text.normal_inverse + + Keys.onSpacePressed: ApplicationModel.openOnlineHelp("readerDeviceTab") + onLinkActivated: Qt.openUrlExternally(link) + + FocusFrame { + dynamic: false + border.color: Constants.black + } + } + + GSeparator { + width: parent.width + } + + RowLayout { + id: hintAndDateText + + width: parent.width + + spacing: Constants.component_spacing + + Image { + Layout.preferredHeight: hintText.height * 1.5 + + fillMode: Image.PreserveAspectFit + source: "qrc:/images/desktop/info_version.svg" + } + + GText { + id: hintText + + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + + activeFocusOnTab: true + + verticalAlignment: Text.AlignBottom + textStyle: Style.text.hint_inverse + text: qsTr("After connecting a new card reader it may take a few seconds to recognize the driver. It may be necessary to restart your system after installing the driver. Only supported and connected card reader are shown here. %1").arg(ReaderDriverModel.lastUpdatedInformation) + SettingsModel.translationTrigger + + FocusFrame { + dynamic: false + border.color: Constants.black + } + } + } +} diff --git a/resources/qml/Governikus/SettingsView/+desktop/DeveloperSettings.qml b/resources/qml/Governikus/SettingsView/+desktop/DeveloperSettings.qml new file mode 100644 index 0000000..e71a88b --- /dev/null +++ b/resources/qml/Governikus/SettingsView/+desktop/DeveloperSettings.qml @@ -0,0 +1,54 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + +ColumnLayout { + + spacing: Constants.component_spacing + + GText { + //: LABEL DESKTOP_QML + text: qsTr("Developer options") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + } + + ToggleableOption { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Test PKI") + SettingsModel.translationTrigger + checked: SettingsModel.useSelfauthenticationTestUri + onCheckedChanged: SettingsModel.useSelfauthenticationTestUri = checked + } + + ToggleableOption { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Developer mode") + SettingsModel.translationTrigger + checked: SettingsModel.developerMode + onCheckedChanged: SettingsModel.developerMode = checked + } + + GText { + Layout.fillWidth: true + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML Only visible when the user activates the developer mode in the settings. + text: qsTr("The developer mode is aimed at integrators / developers for new service applications. For this reason, the developer mode works only in the test PKI. By activating the developer mode, some safety tests are deactivated. This means that the authentication process continues although the AusweisApp2 would usually abort the process with an error message when used in normal operation mode. Information on the disregarded error in the developer mode is displayed in the attached window below the AusweisApp2.") + SettingsModel.translationTrigger + textStyle: Style.text.hint_warning + + FocusFrame { + dynamic: false + border.color: Constants.black + } + } +} diff --git a/resources/qml/Governikus/SettingsView/+desktop/GeneralSettings.qml b/resources/qml/Governikus/SettingsView/+desktop/GeneralSettings.qml new file mode 100644 index 0000000..0648501 --- /dev/null +++ b/resources/qml/Governikus/SettingsView/+desktop/GeneralSettings.qml @@ -0,0 +1,94 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + +ColumnLayout { + + spacing: Constants.component_spacing + + GText { + //: LABEL DESKTOP_QML + text: qsTr("Language selection") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + } + + RowLayout { + spacing: Constants.component_spacing + + LocationButton { + activeFocusOnTab: true + Accessible.name: qsTr("German") + SettingsModel.translationTrigger + Accessible.description: qsTr("Set language to german") + SettingsModel.translationTrigger + + language: "de" + text: "DE" + image: "qrc:///images/location_flag_de.svg" + } + + LocationButton { + activeFocusOnTab: true + Accessible.name: qsTr("English") + SettingsModel.translationTrigger + Accessible.description: qsTr("Set language to english") + SettingsModel.translationTrigger + + language: "en" + text: "EN" + image: "qrc:///images/location_flag_en.svg" + } + + } + + GSeparator { + Layout.fillWidth: true + } + + GText { + //: LABEL DESKTOP_QML + text: qsTr("UI settings") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + } + + GButton { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Switch back to old UI") + SettingsModel.translationTrigger + onClicked: plugin.switchUi() + } + + GSeparator { + Layout.fillWidth: true + } + + GText { + //: LABEL DESKTOP_QML + text: qsTr("Behavior") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + } + + ToggleableOption { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Auto start AusweisApp2 after boot") + SettingsModel.translationTrigger + checked: SettingsModel.autoStartApp + enabled: !SettingsModel.autoStartSetByAdmin + onCheckedChanged: SettingsModel.autoStartApp = checked + } + + ToggleableOption { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Close after authentication") + SettingsModel.translationTrigger + checked: SettingsModel.autoCloseWindowAfterAuthentication + onCheckedChanged: SettingsModel.autoCloseWindowAfterAuthentication = checked + } + +} diff --git a/resources/qml/Governikus/SettingsView/+desktop/RemoteReaderDelegate.qml b/resources/qml/Governikus/SettingsView/+desktop/RemoteReaderDelegate.qml new file mode 100644 index 0000000..61ea10d --- /dev/null +++ b/resources/qml/Governikus/SettingsView/+desktop/RemoteReaderDelegate.qml @@ -0,0 +1,125 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.12 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.View 1.0 + +Item { + id: root + + signal unpairDevice(string pDeviceId) + signal pairDevice(string pDeviceId) + + property int iconHeight: ApplicationModel.scaleFactor * 80 + + implicitHeight: rowLayout.implicitHeight + activeFocusOnTab: true + + FocusFrame { + dynamic: false + border.color: Constants.black + } + + RowLayout { + id: rowLayout + + width: parent.width + + spacing: 0 + + ColumnLayout { + Layout.fillHeight: true + + GText { + Layout.preferredWidth: implicitWidth + textStyle: Style.text.header_inverse + text: remoteDeviceName + } + + GText { + Layout.preferredWidth: implicitWidth + textStyle: Style.text.normal_inverse + text: { + if (!isPaired) { + return qsTr("Click to pair") + SettingsModel.translationTrigger + } + + return remoteDeviceStatus + } + } + } + + Row { + Layout.preferredHeight: iconHeight + Layout.alignment: Qt.AlignRight + + spacing: Constants.component_spacing + + Image { + source: { + if (!isNetworkVisible && isPaired) { + return "qrc:///images/icon_remote_inactive.svg" + } + + if (linkQualityInPercent < 25) { + return "qrc:///images/icon_remote_0.svg" + } + if (linkQualityInPercent < 50) { + return "qrc:///images/icon_remote_25.svg" + } + if (linkQualityInPercent < 75) { + return "qrc:///images/icon_remote_50.svg" + } + if (linkQualityInPercent < 100) { + return "qrc:///images/icon_remote_75.svg" + } + + return "qrc:///images/icon_remote_100.svg" + } + + sourceSize.height: iconHeight + fillMode: Image.PreserveAspectFit + } + + Image { + id: removeIcon + + visible: isPaired + + source: "qrc:///images/trash_icon.svg" + sourceSize.height: iconHeight + fillMode: Image.PreserveAspectFit + + MouseArea { + anchors.fill: parent + onClicked: unpairDevice(deviceId) + cursorShape: Qt.PointingHandCursor + } + } + + Item { + id: spacer + + visible: !removeIcon.visible + + height: removeIcon.height + width: removeIcon.width + } + } + } + + MouseArea { + anchors.fill: parent + + visible: !isPaired + onClicked: pairDevice(deviceId) + cursorShape: Qt.PointingHandCursor + } +} diff --git a/resources/qml/Governikus/SettingsView/+desktop/RemoteReaderView.qml b/resources/qml/Governikus/SettingsView/+desktop/RemoteReaderView.qml new file mode 100644 index 0000000..291e61c --- /dev/null +++ b/resources/qml/Governikus/SettingsView/+desktop/RemoteReaderView.qml @@ -0,0 +1,138 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.12 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Type.NumberModel 1.0 +import Governikus.Type.ReaderScanEnabler 1.0 +import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.View 1.0 + +Item { + id: root + + signal pairDevice(string pDeviceId) + signal unpairDevice(string pDeviceId) + + Component.onCompleted: { + RemoteServiceModel.detectRemoteDevices = visible + } + + Component.onDestruction: { + RemoteServiceModel.detectRemoteDevices = false + } + + onVisibleChanged: RemoteServiceModel.detectRemoteDevices = visible + + Column { + anchors.fill: parent + spacing: Constants.component_spacing + + + GText { + visible: knownDevices.count > 0 + + width: parent.width + + textStyle: Style.text.header_accent + text: qsTr("Paired remote devices") + SettingsModel.translationTrigger + } + + Column { + width: parent.width + + Repeater { + id: knownDevices + + model: RemoteServiceModel.knownDevices + delegate: RemoteReaderDelegate { + width: parent.width + height: implicitHeight + Constants.pane_padding + onUnpairDevice: root.unpairDevice(pDeviceId) + } + } + } + + GSeparator { + visible: knownDevices.count > 0 + + width: parent.width + } + + GText { + width: parent.width + textStyle: Style.text.header_accent + text: qsTr("Available remote devices") + SettingsModel.translationTrigger + } + + Column { + width: parent.width + + Repeater { + id: availableDevices + + model: RemoteServiceModel.availableRemoteDevices + delegate: RemoteReaderDelegate { + width: parent.width + height: implicitHeight + Constants.pane_padding + onPairDevice: root.pairDevice(pDeviceId) + } + } + } + + GText { + visible: availableDevices.count === 0 + + width: parent.width + + activeFocusOnTab: true + textStyle: Style.text.normal_inverse + text: qsTr("No devices with enabled remote service were found on the current WiFi network") + SettingsModel.translationTrigger + + FocusFrame { + dynamic: false + border.color: Constants.black + } + } + + RowLayout { + id: hint + + width: parent.width + + spacing: Constants.component_spacing + + Image { + Layout.preferredHeight: hintText.height * 1.5 + + fillMode: Image.PreserveAspectFit + source: "qrc:/images/desktop/info_version.svg" + } + + GText { + id: hintText + + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + + activeFocusOnTab: true + + wrapMode: Text.WordWrap + verticalAlignment: Text.AlignBottom + textStyle: Style.text.hint_inverse + text: qsTr("Only devices that are already paired or are connected to the same WiFi network and have the remote service enabled are shown here.") + SettingsModel.translationTrigger + + FocusFrame { + dynamic: false + border.color: Constants.black + } + } + } + } +} diff --git a/resources/qml/Governikus/SettingsView/+desktop/SecurityAndPrivacySettings.qml b/resources/qml/Governikus/SettingsView/+desktop/SecurityAndPrivacySettings.qml new file mode 100644 index 0000000..c9eaa8c --- /dev/null +++ b/resources/qml/Governikus/SettingsView/+desktop/SecurityAndPrivacySettings.qml @@ -0,0 +1,95 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick.Layouts 1.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + +ColumnLayout { + + spacing: Constants.component_spacing + + GText { + //: LABEL DESKTOP_QML + text: qsTr("History") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + } + + ToggleableOption { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Save authentification history") + SettingsModel.translationTrigger + checked: SettingsModel.historyEnabled + onCheckedChanged: SettingsModel.historyEnabled = checked + } + + GButton { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Clear entire history") + SettingsModel.translationTrigger + onClicked: SettingsModel.removeEntireHistory() + } + + GSeparator { + Layout.fillWidth: true + } + + GText { + //: LABEL DESKTOP_QML + text: qsTr("Onscreen keypad") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + } + + ToggleableOption { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Use on screen keypad for PIN entry") + SettingsModel.translationTrigger + checked: SettingsModel.useScreenKeyboard + onCheckedChanged: SettingsModel.useScreenKeyboard = checked + } + + ToggleableOption { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Shuffle keypad buttons") + SettingsModel.translationTrigger + checked: SettingsModel.shuffleScreenKeyboard + enabled: SettingsModel.useScreenKeyboard + onCheckedChanged: SettingsModel.shuffleScreenKeyboard = checked + } + + GSeparator { + Layout.fillWidth: true + } + + GText { + //: LABEL DESKTOP_QML + text: qsTr("Software updates") + SettingsModel.translationTrigger + textStyle: Style.text.header_accent + } + + ToggleableOption { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Check at program start") + SettingsModel.translationTrigger + checked: SettingsModel.autoUpdateCheck + enabled: !SettingsModel.autoUpdateCeckSetByAdmin + onCheckedChanged: SettingsModel.autoUpdateCheck = checked + } + + GButton { + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + text: qsTr("Check now") + SettingsModel.translationTrigger + onClicked: SettingsModel.updateApp() + } + +} diff --git a/resources/qml/Governikus/SettingsView/+desktop/SettingsView.qml b/resources/qml/Governikus/SettingsView/+desktop/SettingsView.qml new file mode 100644 index 0000000..2132acc --- /dev/null +++ b/resources/qml/Governikus/SettingsView/+desktop/SettingsView.qml @@ -0,0 +1,144 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQml.Models 2.3 + +import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.UiModule 1.0 +import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.EnterPasswordView 1.0 +import Governikus.Type.NumberModel 1.0 + + +SectionPage { + id: sectionPage + + enum SubView { + None, + EnterPassword, + PairingInfo + } + + Accessible.name: qsTr("Settings view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the settings panel of the AusweisApp2.") + SettingsModel.translationTrigger + Keys.onEscapePressed: { + if (d.view === SettingsView.SubView.None) { + event.accepted = false + return + } + + d.view = SettingsView.SubView.None + } + + isAbstract: d.view !== SettingsView.SubView.None + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Settings") + SettingsModel.translationTrigger + + onClicked: { + d.view = SettingsView.SubView.None + } + } + + QtObject { + id: d + + property int view: SettingsView.SubView.None + } + + TabbedPane { + id: tabbedPane + + visible: d.view === SettingsView.SubView.None + anchors.fill: parent + anchors.margins: Constants.pane_spacing + + sectionsModel: { + var model = [ + //: LABEL DESKTOP_QML + qsTr("General") + SettingsModel.translationTrigger, + //: LABEL DESKTOP_QML + qsTr("Smartphone as card reader") + SettingsModel.translationTrigger, + //: LABEL DESKTOP_QML + qsTr("USB card reader") + SettingsModel.translationTrigger, + //: LABEL DESKTOP_QML + qsTr("Security and privacy") + SettingsModel.translationTrigger + ] + + if (plugin.developerBuild) { + model.push( + //: LABEL DESKTOP_QML + qsTr("Developer options") + SettingsModel.translationTrigger + ) + } + + return model + } + + sectionDelegate: TabbedPaneDelegateOneLineText { + sectionName: model ? model.modelData : "" + } + + contentObjectModel: ObjectModel { + Component { GeneralSettings {} } + Component { + RemoteReaderView { + width: parent.width + height: Math.max(implicitHeight, tabbedPane.availableHeight) + onPairDevice: { + if (RemoteServiceModel.rememberServer(pDeviceId)) { + d.view = TabbedReaderView.SubView.EnterPassword + appWindow.menuBar.updateActions() + } + } + onUnpairDevice: RemoteServiceModel.forgetDevice(pDeviceId) + } + } + Component { + CardReaderView { + width: parent.width + height: Math.max(implicitHeight, tabbedPane.availableHeight) + } + } + Component { SecurityAndPrivacySettings {} } + Component { DeveloperSettings {} } + } + } + + EnterPasswordView { + id: enterPassword + + visible: d.view === SettingsView.SubView.EnterPassword + + statusIcon: "qrc:///images/phone_to_pc.svg" + passwordType: NumberModel.PASSWORD_REMOTE_PIN + + onPasswordEntered: d.view = SettingsView.SubView.None + + onRequestPasswordInfo: { + d.view = SettingsView.SubView.PairingInfo + appWindow.menuBar.updateActions() + } + } + + PasswordInfoView { + id: passwordInfoView + + visible: d.view === SettingsView.SubView.PairingInfo + + passwordType: NumberModel.PASSWORD_REMOTE_PIN + + onClose: { + d.view = SettingsView.SubView.EnterPassword + appWindow.menuBar.updateActions() + } + } +} diff --git a/resources/qml/Governikus/SettingsView/+desktop/TabbedReaderView.qml b/resources/qml/Governikus/SettingsView/+desktop/TabbedReaderView.qml new file mode 100644 index 0000000..47ece8d --- /dev/null +++ b/resources/qml/Governikus/SettingsView/+desktop/TabbedReaderView.qml @@ -0,0 +1,140 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQml.Models 2.10 + +import Governikus.EnterPasswordView 1.0 +import Governikus.Global 1.0 +import Governikus.TitleBar 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.NumberModel 1.0 +import Governikus.Type.RemoteServiceModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.View 1.0 + +SectionPage { + id: root + + property alias rootEnabled: titleBarAction.rootEnabled + property bool showPairingInfoPopup: false + signal closeView() + + enum SubView { + None, + EnterPassword, + PairingInfo + } + + QtObject { + id: d + + property int view + } + + onVisibleChanged: d.view = TabbedReaderView.SubView.None + + titleBarAction: TitleBarAction { + id:titleBarAction + + //: LABEL DESKTOP_QML + text: qsTr("Card Readers") + SettingsModel.translationTrigger + helpTopic: "readerDeviceTab" + rootEnabled: false + customSubAction: CancelAction { + visible: d.view === TabbedReaderView.SubView.EnterPassword + + onClicked: d.view = TabbedReaderView.SubView.None + } + } + + TabbedPane { + id: tabbedPane + + visible: d.view === TabbedReaderView.SubView.None + + anchors.fill: parent + anchors.margins: Constants.pane_padding + + sectionsModel: [ + qsTr("Smartphone as card reader") + SettingsModel.translationTrigger, + qsTr("USB card reader") + SettingsModel.translationTrigger + ] + contentObjectModel: ObjectModel { + Component { + RemoteReaderView { + width: parent.width + height: Math.max(implicitHeight, tabbedPane.availableHeight) + onPairDevice: { + if (RemoteServiceModel.rememberServer(pDeviceId)) { + if (showPairingInfoPopup) { + pairingInfo.open() + } + else { + d.view = TabbedReaderView.SubView.EnterPassword + appWindow.menuBar.updateActions() + } + } + } + onUnpairDevice: RemoteServiceModel.forgetDevice(pDeviceId) + } + } + + Component { + CardReaderView { + width: parent.width + height: Math.max(implicitHeight, tabbedPane.availableHeight) + } + } + } + footerItem: Item { + height: childrenRect.height + + NavigationButton { + buttonType: Qt.BackButton + onClicked: root.closeView() + } + } + } + + ConfirmationPopup { + id: pairingInfo + + //: LABEL DESKTOP_QML + text: qsTr("Please start pairing mode first.") + SettingsModel.translationTrigger + onConfirmed: { + d.view = TabbedReaderView.SubView.EnterPassword + appWindow.menuBar.updateActions() + } + } + + EnterPasswordView { + id: enterPassword + + visible: d.view === TabbedReaderView.SubView.EnterPassword + + statusIcon: "qrc:///images/phone_to_pc.svg" + passwordType: NumberModel.PASSWORD_REMOTE_PIN + + onPasswordEntered: d.view = TabbedReaderView.SubView.None + + onRequestPasswordInfo: { + d.view = TabbedReaderView.SubView.PairingInfo + appWindow.menuBar.updateActions() + } + } + + PasswordInfoView { + id: passwordInfoView + + visible: d.view === TabbedReaderView.SubView.PairingInfo + + passwordType: NumberModel.PASSWORD_REMOTE_PIN + + onClose: { + d.view = TabbedReaderView.SubView.EnterPassword + appWindow.menuBar.updateActions() + } + } +} diff --git a/resources/qml/Governikus/SettingsView/qmldir b/resources/qml/Governikus/SettingsView/qmldir new file mode 100644 index 0000000..cfab5a3 --- /dev/null +++ b/resources/qml/Governikus/SettingsView/qmldir @@ -0,0 +1,12 @@ +module SettingsView + +internal CardReaderDelegate CardReaderDelegate.qml +internal RemoteReaderDelegate RemoteReaderDelegate.qml +internal GeneralSettings GeneralSettings.qml +internal DeveloperSettings DeveloperSettings.qml +internal SecurityAndPrivacySettings SecurityAndPrivacySettings.qml + +CardReaderView 1.0 CardReaderView.qml +RemoteReaderView 1.0 RemoteReaderView.qml +SettingsView 1.0 SettingsView.qml +TabbedReaderView 1.0 TabbedReaderView.qml diff --git a/resources/qml/Governikus/SplashScreen/SplashScreen.qml b/resources/qml/Governikus/SplashScreen/SplashScreen.qml index 2772b5b..70c1830 100644 --- a/resources/qml/Governikus/SplashScreen/SplashScreen.qml +++ b/resources/qml/Governikus/SplashScreen/SplashScreen.qml @@ -1,5 +1,11 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import Governikus.Style 1.0 + Rectangle { id: splashScreen visible: true @@ -18,6 +24,19 @@ Rectangle { visible: parent.visible } + Rectangle { + id: titleBar + + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: plugin.safeAreaMargins.top + + color: Style.color.accent + } + function hide() { if (!splashScreen.visible) { return diff --git a/resources/qml/Governikus/Style/+desktop/PlatformColors.qml b/resources/qml/Governikus/Style/+desktop/PlatformColors.qml new file mode 100644 index 0000000..f2dfffd --- /dev/null +++ b/resources/qml/Governikus/Style/+desktop/PlatformColors.qml @@ -0,0 +1,24 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQml 2.10 + +QtObject { + readonly property color border: "#bbbbbb" + + readonly property color background: "#659bcd" + readonly property color background_popup: background + readonly property color background_pane: "#ffffff" + readonly property color background_pane_active: "#d0e1f0" + + readonly property color accent: "#5481ab" + readonly property color accent_disabled: "#d0d0d0" + + readonly property color primary_text: "#ffffff" + readonly property color primary_text_inverse: "#333333" + readonly property color secondary_text: "#dadada" + readonly property color secondary_text_inverse: "#666666" + readonly property color warning_text: "#cc0000" + readonly property color button_text: "#ffffff" +} diff --git a/resources/qml/Governikus/Style/+desktop/PlatformDimensions.qml b/resources/qml/Governikus/Style/+desktop/PlatformDimensions.qml new file mode 100644 index 0000000..3b2d3f6 --- /dev/null +++ b/resources/qml/Governikus/Style/+desktop/PlatformDimensions.qml @@ -0,0 +1,25 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQml 2.10 +import Governikus.Type.ApplicationModel 1.0 + +QtObject { + readonly property int title_font_size: ApplicationModel.scaleFactor * 42 + readonly property int header_font_size: ApplicationModel.scaleFactor * 32 + readonly property int normal_font_size: ApplicationModel.scaleFactor * 26 + readonly property int hint_font_size: ApplicationModel.scaleFactor * 20 + + readonly property int corner_radius: ApplicationModel.scaleFactor * 20 + readonly property int corner_radius_popup: corner_radius + readonly property int button_radius: ApplicationModel.scaleFactor * 15 + readonly property int separator_size: Math.max(ApplicationModel.scaleFactor * 2, 1) + readonly property real max_text_width: ApplicationModel.scaleFactor * 1000 + + readonly property int titlebar_padding: ApplicationModel.scaleFactor * 20 + + readonly property int status_icon_large: ApplicationModel.scaleFactor * 350 + readonly property int status_icon_medium: ApplicationModel.scaleFactor * 200 + readonly property int status_icon_small: ApplicationModel.scaleFactor * 100 +} diff --git a/resources/qml/Governikus/Style/+desktop/PlatformTextStyles.qml b/resources/qml/Governikus/Style/+desktop/PlatformTextStyles.qml new file mode 100644 index 0000000..1ffb076 --- /dev/null +++ b/resources/qml/Governikus/Style/+desktop/PlatformTextStyles.qml @@ -0,0 +1,8 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +QtObject { +} diff --git a/resources/qml/Governikus/Style/+desktop/ProviderStyle.qml b/resources/qml/Governikus/Style/+desktop/ProviderStyle.qml deleted file mode 100644 index fa889c8..0000000 --- a/resources/qml/Governikus/Style/+desktop/ProviderStyle.qml +++ /dev/null @@ -1,7 +0,0 @@ -pragma Singleton - -import QtQuick 2.10 - - -Item { -} diff --git a/resources/qml/Governikus/Style/+mobile/+android/BrandColors.qml b/resources/qml/Governikus/Style/+mobile/+android/BrandColors.qml new file mode 100644 index 0000000..1988191 --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/+android/BrandColors.qml @@ -0,0 +1,8 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQml 2.10 + +QtObject { +} diff --git a/resources/qml/Governikus/Style/+mobile/+android/BrandDimensions.qml b/resources/qml/Governikus/Style/+mobile/+android/BrandDimensions.qml new file mode 100644 index 0000000..2414bbb --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/+android/BrandDimensions.qml @@ -0,0 +1,19 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import Governikus.Global 1.0 + +QtObject { + readonly property int corner_radius: 20 + readonly property int corner_radius_popup: 2 + + readonly property int button_height: 36 + readonly property int menubar_width: 64 + + readonly property int title_font_size: 22 + readonly property int header_font_size: 20 + readonly property int normal_font_size: 16 + readonly property int hint_font_size: 14 +} diff --git a/resources/qml/Governikus/Style/+mobile/+android/PlatformTextStyles.qml b/resources/qml/Governikus/Style/+mobile/+android/PlatformTextStyles.qml new file mode 100644 index 0000000..60874d2 --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/+android/PlatformTextStyles.qml @@ -0,0 +1,23 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +QtObject { + readonly property var tutorial_title: TextStyle { + textSize: Style.dimens.tutorial_title_font_size + } + readonly property var tutorial_header: TextStyle { + textSize: Style.dimens.tutorial_header_font_size + textFamily: "Noto Serif" + } + readonly property var tutorial_header_secondary: TextStyle { + textSize: Style.dimens.header_font_size + textFamily: "Noto Serif" + } + readonly property var tutorial_content: TextStyle { + textSize: Style.dimens.normal_font_size + textFamily: "Noto Serif" + } +} diff --git a/resources/qml/Governikus/Style/+mobile/+android/ProviderStyle.qml b/resources/qml/Governikus/Style/+mobile/+android/ProviderStyle.qml deleted file mode 100644 index 5876377..0000000 --- a/resources/qml/Governikus/Style/+mobile/+android/ProviderStyle.qml +++ /dev/null @@ -1,35 +0,0 @@ -pragma Singleton - -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Item { - // Provider category list properties - readonly property int categoryFontPixelSize: Constants.titlebar_font_size - readonly property bool categoryFontBold: true - readonly property color categoryColor: Constants.accent_color - readonly property int leftIconMargin: Utils.dp(10) - readonly property int leftProviderListMargin: Utils.dp(20) - readonly property bool showCategoryRightArrow: false - - // Provider list item properties - readonly property int itemLeftMargin: Utils.dp(15) - readonly property color subjectTextColor: Constants.secondary_text - readonly property bool subjectTextFontBold: true - readonly property int addressTextFontSize: Utils.dp(12) - readonly property color addressTextColor: Constants.accent_color - readonly property double infoItemWidthFactor: 4.0 - - readonly property int providerListItemTopMargin: Utils.dp(0) - readonly property int providerListItemRightMargin: Utils.dp(0) - readonly property int providerListItemBottomMargin: Utils.dp(0) - - readonly property bool providerListItemsHaveBorder: true - - readonly property bool providerListDetailsLinkBold: true - readonly property color providerListDetailsLinkBorder: Constants.grey_light - readonly property color providerListDetailsLinkColor: Constants.primary_text - readonly property color providerListDetailsLinkBackground: Constants.grey_light - readonly property string providerListDetailsLinkPosition: "top" -} diff --git a/resources/qml/Governikus/Style/+mobile/+ios/BrandColors.qml b/resources/qml/Governikus/Style/+mobile/+ios/BrandColors.qml new file mode 100644 index 0000000..1988191 --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/+ios/BrandColors.qml @@ -0,0 +1,8 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQml 2.10 + +QtObject { +} diff --git a/resources/qml/Governikus/Style/+mobile/+ios/BrandDimensions.qml b/resources/qml/Governikus/Style/+mobile/+ios/BrandDimensions.qml new file mode 100644 index 0000000..db2bfcb --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/+ios/BrandDimensions.qml @@ -0,0 +1,30 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Window 2.2 +import Governikus.Global 1.0 + +QtObject { + readonly property int corner_radius: 10 + readonly property int corner_radius_popup: corner_radius + + readonly property int button_height: 40 + readonly property int tabbar_height: 49 + readonly property int searchbar_height: 48 + + readonly property int title_font_size: scaleText(22) + readonly property int header_font_size: scaleText(20) + readonly property int normal_font_size: scaleText(16) + readonly property int hint_font_size: scaleText(14) + + // Scale the text on small devices like the iPhone SE + function scaleText(value) { + var w = Screen.width + if (w > 415) { + return value + } + return value * w / 415 + } +} diff --git a/resources/qml/Governikus/Style/+mobile/+ios/PlatformTextStyles.qml b/resources/qml/Governikus/Style/+mobile/+ios/PlatformTextStyles.qml new file mode 100644 index 0000000..77378f3 --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/+ios/PlatformTextStyles.qml @@ -0,0 +1,23 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +QtObject { + readonly property var tutorial_title: TextStyle { + textSize: Style.dimens.tutorial_title_font_size + } + readonly property var tutorial_header: TextStyle { + textSize: Style.dimens.tutorial_header_font_size + textFamily: "Charter" + } + readonly property var tutorial_header_secondary: TextStyle { + textSize: Style.dimens.header_font_size + textFamily: "Charter" + } + readonly property var tutorial_content: TextStyle { + textSize: Style.dimens.normal_font_size + textFamily: "Charter" + } +} diff --git a/resources/qml/Governikus/Style/+mobile/+ios/ProviderStyle.qml b/resources/qml/Governikus/Style/+mobile/+ios/ProviderStyle.qml deleted file mode 100644 index 1e256ef..0000000 --- a/resources/qml/Governikus/Style/+mobile/+ios/ProviderStyle.qml +++ /dev/null @@ -1,35 +0,0 @@ -pragma Singleton - -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Item { - // Provider category list properties - readonly property int categoryFontPixelSize: Constants.normal_font_size - readonly property bool categoryFontBold: false - readonly property color categoryColor: Constants.accent_color - readonly property int leftIconMargin: Utils.dp(0) - readonly property int leftProviderListMargin: Utils.dp(0) - readonly property bool showCategoryRightArrow: true - - // Provider list item properties - readonly property int itemLeftMargin: Utils.dp(5) - readonly property color subjectTextColor: "#000000" // default color, which is? - readonly property bool subjectTextFontBold: false - readonly property int addressTextFontSize: Utils.dp(11) - readonly property color addressTextColor: Constants.blue_dark - readonly property double infoItemWidthFactor: 2.0 - - readonly property int providerListItemTopMargin: Utils.dp(2) - readonly property int providerListItemRightMargin: Utils.dp(5) - readonly property int providerListItemBottomMargin: Utils.dp(5) - - readonly property bool providerListItemsHaveBorder: true - - readonly property bool providerListDetailsLinkBold: false - readonly property color providerListDetailsLinkBorder: Constants.blue_dark - readonly property color providerListDetailsLinkColor: Constants.blue_dark - readonly property color providerListDetailsLinkBackground: "#ffffff" - readonly property string providerListDetailsLinkPosition: "middle" -} diff --git a/resources/qml/Governikus/Style/+mobile/PlatformColors.qml b/resources/qml/Governikus/Style/+mobile/PlatformColors.qml new file mode 100644 index 0000000..b1ddf3f --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/PlatformColors.qml @@ -0,0 +1,31 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +BrandColors { + readonly property color border: "#ebebeb" + readonly property color border_dark: "#dadada" + + readonly property color background: "#dcebf6" + readonly property color background_popup: "#ffffff" + readonly property color background_pane: "#ffffff" + + readonly property color accent: "#659bcd" + readonly property color accent_disabled: "#d0d0d0" + + readonly property color primary_text: "#333333" + readonly property color primary_text_inverse: "#ffffff" + readonly property color secondary_text: "#666666" + readonly property color secondary_text_inverse: "#dadada" + readonly property color warning_text: "#cc0000" + readonly property color button_text: "#ffffff" + + readonly property color tutorial_what: "#f9a501" + readonly property color tutorial_where: "#73d7b3" + readonly property color tutorial_how: "#659bcd" + readonly property color tutorial_important: "#fb7a59" + readonly property color tutorial_box_background: "#f2f2f2" + + readonly property color card_reader: "#444445" + readonly property color id_card: "#77add7" +} diff --git a/resources/qml/Governikus/Style/+mobile/PlatformDimensions.qml b/resources/qml/Governikus/Style/+mobile/PlatformDimensions.qml new file mode 100644 index 0000000..b2cd21e --- /dev/null +++ b/resources/qml/Governikus/Style/+mobile/PlatformDimensions.qml @@ -0,0 +1,25 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQml 2.10 +import Governikus.Global 1.0 + +BrandDimensions { + readonly property int button_radius: 3 + readonly property int separator_size: 1 + readonly property int list_item_height: 64 + readonly property int scrollbar_padding: 5 + readonly property int tutorial_component_spacing: 40 + + readonly property int titlebar_height: 48 + readonly property int titlebar_padding: 8 + + readonly property real max_text_width: 500 + + readonly property int small_icon_size: 24 + readonly property int icon_size: 48 + + readonly property int tutorial_title_font_size: 60 + readonly property int tutorial_header_font_size: 26 +} diff --git a/resources/qml/Governikus/Style/Colors.qml b/resources/qml/Governikus/Style/Colors.qml new file mode 100644 index 0000000..1637926 --- /dev/null +++ b/resources/qml/Governikus/Style/Colors.qml @@ -0,0 +1,8 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + + +PlatformColors { + readonly property color transparent: "transparent" +} diff --git a/resources/qml/Governikus/Style/Dimensions.qml b/resources/qml/Governikus/Style/Dimensions.qml new file mode 100644 index 0000000..0b0e2fd --- /dev/null +++ b/resources/qml/Governikus/Style/Dimensions.qml @@ -0,0 +1,7 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +PlatformDimensions { + readonly property real minimumScrollBarSize: 0.05 +} diff --git a/resources/qml/Governikus/Style/NpaBusyIndicatorStyle.qml b/resources/qml/Governikus/Style/NpaBusyIndicatorStyle.qml index 69a6ded..3e1281a 100644 --- a/resources/qml/Governikus/Style/NpaBusyIndicatorStyle.qml +++ b/resources/qml/Governikus/Style/NpaBusyIndicatorStyle.qml @@ -1,8 +1,13 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQml 2.2 import QtGraphicalEffects 1.0 import Governikus.Global 1.0 +import Governikus.Style 1.0 Item { property real factor: 1.1 @@ -44,7 +49,7 @@ import Governikus.Global 1.0 height: parent.height * factor width: height radius: width / 2 - color: Constants.background_color + color: Style.color.background opacity: 0 Behavior on opacity { @@ -73,8 +78,8 @@ import Governikus.Global 1.0 { GradientStop {color: Constants.green; position: 0.0} GradientStop {color: Constants.green; position: 0.50} - GradientStop {color: "transparent"; position: 0.5000000000000001} - GradientStop {color: "transparent"; position: 1.0} + GradientStop {color: Style.color.transparent; position: 0.5000000000000001} + GradientStop {color: Style.color.transparent; position: 1.0} } Behavior on rotation { NumberAnimation { duration: timer.interval; easing.type: Easing.InOutQuad } @@ -89,8 +94,8 @@ import Governikus.Global 1.0 opacity: rect.opacity gradient: Gradient { - GradientStop {color: "transparent"; position: 0.0} - GradientStop {color: "transparent"; position: 0.50} + GradientStop {color: Style.color.transparent; position: 0.0} + GradientStop {color: Style.color.transparent; position: 0.50} GradientStop {color: rect.color; position: 0.5000000000000001} GradientStop {color: Constants.blue; position: 1.0} } diff --git a/resources/qml/Governikus/Style/Style.qml b/resources/qml/Governikus/Style/Style.qml new file mode 100644 index 0000000..19970c0 --- /dev/null +++ b/resources/qml/Governikus/Style/Style.qml @@ -0,0 +1,13 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +pragma Singleton + +import QtQml 2.10 + +QtObject { + property var text: TextStyles {} + property var color: Colors {} + property var dimens: Dimensions {} +} diff --git a/resources/qml/Governikus/Style/TextStyle.qml b/resources/qml/Governikus/Style/TextStyle.qml new file mode 100644 index 0000000..4002b28 --- /dev/null +++ b/resources/qml/Governikus/Style/TextStyle.qml @@ -0,0 +1,13 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +QtObject { + property color textColor: Style.color.primary_text + property int textSize: Style.dimens.normal_font_size + + /// An empty string means "unspecified" + property string textFamily +} diff --git a/resources/qml/Governikus/Style/TextStyles.qml b/resources/qml/Governikus/Style/TextStyles.qml new file mode 100644 index 0000000..35cea28 --- /dev/null +++ b/resources/qml/Governikus/Style/TextStyles.qml @@ -0,0 +1,115 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +PlatformTextStyles { + + readonly property var title: TextStyle { + textSize: Style.dimens.title_font_size + } + + readonly property var title_secondary: TextStyle { + textSize: Style.dimens.title_font_size + textColor: Style.color.secondary_text + } + + readonly property var title_accent: TextStyle { + textSize: Style.dimens.title_font_size + textColor: Style.color.accent + } + + readonly property var title_warning: TextStyle { + textSize: Style.dimens.title_font_size + textColor: Style.color.warning_text + } + + readonly property var header: TextStyle { + textSize: Style.dimens.header_font_size + } + + readonly property var header_inverse: TextStyle { + textSize: Style.dimens.header_font_size + textColor: Style.color.primary_text_inverse + } + + readonly property var header_secondary: TextStyle { + textSize: Style.dimens.header_font_size + textColor: Style.color.secondary_text + } + + readonly property var header_secondary_inverse: TextStyle { + textSize: Style.dimens.header_font_size + textColor: Style.color.secondary_text_inverse + } + + readonly property var header_accent: TextStyle { + textSize: Style.dimens.header_font_size + textColor: Style.color.accent + } + + readonly property var header_warning: TextStyle { + textSize: Style.dimens.header_font_size + textColor: Style.color.warning_text + } + + readonly property var normal: TextStyle {} + + readonly property var normal_inverse: TextStyle { + textColor: Style.color.primary_text_inverse + } + + readonly property var normal_secondary: TextStyle { + textColor: Style.color.secondary_text + } + + readonly property var normal_secondary_inverse: TextStyle { + textColor: Style.color.secondary_text_inverse + } + + readonly property var normal_accent: TextStyle { + textColor: Style.color.accent + } + + readonly property var normal_warning: TextStyle { + textColor: Style.color.warning_text + } + + readonly property var hint: TextStyle { + textSize: Style.dimens.hint_font_size + } + + readonly property var hint_inverse: TextStyle { + textSize: Style.dimens.hint_font_size + textColor: Style.color.primary_text_inverse + } + + readonly property var hint_secondary: TextStyle { + textSize: Style.dimens.hint_font_size + textColor: Style.color.secondary_text + } + + readonly property var hint_secondary_inverse: TextStyle { + textSize: Style.dimens.hint_font_size + textColor: Style.color.secondary_text_inverse + } + + readonly property var hint_accent: TextStyle { + textSize: Style.dimens.hint_font_size + textColor: Style.color.accent + } + + readonly property var hint_warning: TextStyle { + textSize: Style.dimens.hint_font_size + textColor: Style.color.warning_text + } + + readonly property var button: TextStyle { + textColor: Style.color.button_text + } + + readonly property var button_disabled: TextStyle { + textColor: Style.color.secondary_text_inverse + } +} diff --git a/resources/qml/Governikus/Style/qmldir b/resources/qml/Governikus/Style/qmldir index 4deea53..9950fbd 100644 --- a/resources/qml/Governikus/Style/qmldir +++ b/resources/qml/Governikus/Style/qmldir @@ -1,5 +1,15 @@ module Style -singleton ProviderStyle 1.0 ProviderStyle.qml +internal BrandColors BrandColors.qml +internal BrandDimensions BrandDimensions.qml +internal Colors Colors.qml +internal PlatformColors PlatformColors.qml +internal TextStyles TextStyles.qml +internal PlatformTextStyles PlatformTextStyles.qml +internal Dimensions Dimensions.qml +internal PlatformDimensions PlatformDimensions.qml + +singleton Style 1.0 Style.qml NpaBusyIndicatorStyle 1.0 NpaBusyIndicatorStyle.qml +TextStyle 1.0 TextStyle.qml diff --git a/resources/qml/Governikus/TechnologyInfo/+ios/TechnologySwitch.qml b/resources/qml/Governikus/TechnologyInfo/+ios/TechnologySwitch.qml deleted file mode 100644 index 98aaddd..0000000 --- a/resources/qml/Governikus/TechnologyInfo/+ios/TechnologySwitch.qml +++ /dev/null @@ -1,35 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 -import Governikus.Type.ReaderPlugIn 1.0 - -Rectangle { - id: baseItem - height: technologyRow.height - color: Constants.background_color - - signal requestPluginType(int pReaderPlugInType) - - property int selectedTechnology - - Row { - id: technologyRow - spacing: Utils.dp(20) - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - - TechnologySwitchButton { - buttonActive: selectedTechnology !== ReaderPlugIn.REMOTE - onClicked: baseItem.requestPluginType(ReaderPlugIn.REMOTE) - imageSource: "qrc:///images/icon_remote.svg" - text: qsTr("Use WiFi card reader instead
of Bluetooth card reader") + settingsModel.translationTrigger - } - - TechnologySwitchButton { - buttonActive: selectedTechnology !== ReaderPlugIn.BLUETOOTH - onClicked: baseItem.requestPluginType(ReaderPlugIn.BLUETOOTH) - imageSource: "qrc:///images/icon_bluetooth.svg" - text: qsTr("Use Bluetooth card reader instead
of remote card reader") + settingsModel.translationTrigger - } - } -} diff --git a/resources/qml/Governikus/TechnologyInfo/TechnologyInfo.qml b/resources/qml/Governikus/TechnologyInfo/+mobile/TechnologyInfo.qml similarity index 79% rename from resources/qml/Governikus/TechnologyInfo/TechnologyInfo.qml rename to resources/qml/Governikus/TechnologyInfo/+mobile/TechnologyInfo.qml index 98597f0..e62fe21 100644 --- a/resources/qml/Governikus/TechnologyInfo/TechnologyInfo.qml +++ b/resources/qml/Governikus/TechnologyInfo/+mobile/TechnologyInfo.qml @@ -1,6 +1,12 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 import Governikus.Type.NumberModel 1.0 @@ -14,18 +20,20 @@ Item { signal enableClicked() - Text { + GText { id: enableInfo + + visible: !!text anchors.bottom: enableButton.top - anchors.bottomMargin: Constants.component_spacing + anchors.margins: Constants.component_spacing anchors.left: parent.left anchors.right: parent.right + + Accessible.name: ApplicationModel.stripHtmlTags(text) + verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.normal_font_size - color: Constants.red - wrapMode: Text.WordWrap - visible: !!text + textStyle: Style.text.normal_warning Behavior on text { SequentialAnimation { @@ -44,15 +52,17 @@ Item { onClicked: parent.enableClicked() } - Text { + GText { id: title + + visible: !enableInfo.visible && !enableButton.visible anchors.bottom: parent.verticalCenter anchors.bottomMargin: Constants.component_spacing anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: Constants.header_font_size - font.weight: Font.Bold - color: Constants.blue - visible: !enableInfo.visible && !enableButton.visible + + Accessible.name: ApplicationModel.stripHtmlTags(text) + + textStyle: Style.text.header_accent Behavior on text { SequentialAnimation { @@ -71,26 +81,27 @@ Item { } } - Text { + GText { id: subTitle + + visible: !enableInfo.visible && !enableButton.visible anchors.left: parent.left anchors.top: title.bottom anchors.right: parent.right anchors.bottom: enableButton.top + + Accessible.name: ApplicationModel.stripHtmlTags(text) + horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignTop - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - wrapMode: Text.WordWrap - visible: !enableInfo.visible && !enableButton.visible - + textStyle: Style.text.normal_secondary Behavior on text { SequentialAnimation { PropertyAnimation { target: subTitle; property: "opacity"; to: 0; duration: 500} PropertyAction { target: subTitle; property: "text" } PropertyAnimation { target: subTitle; property: "opacity"; to: 1.0; duration: 500 } - PropertyAction { target: subTitle; property: "color"; value: NumberModel.hasError ? Constants.red : Constants.secondary_text } + PropertyAction { target: subTitle; property: "color"; value: NumberModel.hasError ? Style.color.warning_text : Style.color.secondary_text } } } } diff --git a/resources/qml/Governikus/TechnologyInfo/+android/TechnologySwitch.qml b/resources/qml/Governikus/TechnologyInfo/+mobile/TechnologySwitch.qml similarity index 69% rename from resources/qml/Governikus/TechnologyInfo/+android/TechnologySwitch.qml rename to resources/qml/Governikus/TechnologyInfo/+mobile/TechnologySwitch.qml index a62f492..f8ec4f1 100644 --- a/resources/qml/Governikus/TechnologyInfo/+android/TechnologySwitch.qml +++ b/resources/qml/Governikus/TechnologyInfo/+mobile/TechnologySwitch.qml @@ -1,12 +1,18 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ReaderPlugIn 1.0 -Rectangle { + +Item { id: baseItem height: technologyRow.height - color: Constants.background_color signal requestPluginType(int pReaderPlugInType) @@ -14,7 +20,7 @@ Rectangle { Row { id: technologyRow - spacing: Utils.dp(20) + spacing: 20 anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter @@ -22,21 +28,24 @@ Rectangle { buttonActive: selectedTechnology !== ReaderPlugIn.NFC onClicked: baseItem.requestPluginType(ReaderPlugIn.NFC) imageSource: "qrc:///images/icon_nfc.svg" - text: qsTr("NFC") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("NFC") + SettingsModel.translationTrigger } TechnologySwitchButton { buttonActive: selectedTechnology !== ReaderPlugIn.REMOTE onClicked: baseItem.requestPluginType(ReaderPlugIn.REMOTE) imageSource: "qrc:///images/icon_remote.svg" - text: qsTr("WiFi") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("WiFi") + SettingsModel.translationTrigger } TechnologySwitchButton { buttonActive: selectedTechnology !== ReaderPlugIn.BLUETOOTH onClicked: baseItem.requestPluginType(ReaderPlugIn.BLUETOOTH) imageSource: "qrc:///images/icon_bluetooth.svg" - text: qsTr("Bluetooth") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Bluetooth") + SettingsModel.translationTrigger } } } diff --git a/resources/qml/Governikus/TechnologyInfo/TechnologySwitchButton.qml b/resources/qml/Governikus/TechnologyInfo/+mobile/TechnologySwitchButton.qml similarity index 67% rename from resources/qml/Governikus/TechnologyInfo/TechnologySwitchButton.qml rename to resources/qml/Governikus/TechnologyInfo/+mobile/TechnologySwitchButton.qml index fa33b93..ba5b888 100644 --- a/resources/qml/Governikus/TechnologyInfo/TechnologySwitchButton.qml +++ b/resources/qml/Governikus/TechnologyInfo/+mobile/TechnologySwitchButton.qml @@ -1,38 +1,51 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 import Governikus.Global 1.0 +import Governikus.Style 1.0 MouseArea { + id: root + property alias imageSource: img.source property alias text: infoText.text property bool buttonActive height: img.height + infoText.height + 2 * img.anchors.topMargin + infoText.anchors.topMargin - width: img.width + 2 * Utils.dp(10) + width: img.width + 2 * 10 - Rectangle { - height: 1 + Accessible.role: Accessible.Button + Accessible.name: text + Accessible.onPressAction: if (Qt.platform.os === "ios") clicked(null) + + GSeparator { width: parent.width * 1.5 anchors.horizontalCenter: parent.horizontalCenter - color: Constants.grey + color: Style.color.border_dark } Image { id: img anchors.top: parent.top - anchors.topMargin: Utils.dp(20) + anchors.topMargin: 20 anchors.horizontalCenter: parent.horizontalCenter - height: Utils.dp(50) + sourceSize.height: 50 fillMode: Image.PreserveAspectFit smooth: true } - Text { + GText { id: infoText + anchors.top: img.bottom - anchors.topMargin: Utils.dp(10) + anchors.topMargin: 10 anchors.horizontalCenter: img.horizontalCenter - font.pixelSize: Constants.normal_font_size - color: Constants.blue + + Accessible.ignored: true + + textStyle: Style.text.normal_accent } Colorize { id: grayLevel diff --git a/resources/qml/Governikus/TitleBar/+desktop/CancelAction.qml b/resources/qml/Governikus/TitleBar/+desktop/CancelAction.qml index 4e29099..d8e281c 100644 --- a/resources/qml/Governikus/TitleBar/+desktop/CancelAction.qml +++ b/resources/qml/Governikus/TitleBar/+desktop/CancelAction.qml @@ -1,9 +1,15 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 Button { @@ -11,7 +17,7 @@ Button { visible: ApplicationModel.currentWorkflow !== "" Accessible.role: Accessible.Button - Accessible.name: qsTr("Cancel") + Accessible.name: qsTr("Cancel") + SettingsModel.translationTrigger background: Row { id: row @@ -31,23 +37,30 @@ Button { id: text height: sizeBase.height width: sizeBase.width - readonly property Text sizeBase: Text { - font.weight: Font.Bold - font.pixelSize: Constants.titlebar_font_size - text: qsTr("Cancel") + settingsModel.translationTrigger + readonly property GText sizeBase: GText { + text: qsTr("Cancel") + SettingsModel.translationTrigger + textStyle: Style.text.header + font.bold: true } FocusFrame { scope: button } - Text { + GText { anchors.centerIn: parent - color: Constants.white - font.weight: Font.Bold - font.pixelSize: Constants.titlebar_font_size * (button.down ? 0.9 : 1) + text: text.sizeBase.text + textStyle: Style.text.header + font.bold: true + font.pixelSize: Style.text.header.textSize * (button.down ? 0.9 : 1) } } } + + MouseArea { + cursorShape: Qt.PointingHandCursor + anchors.fill: parent + onPressed: mouse.accepted = false + } } diff --git a/resources/qml/Governikus/TitleBar/+desktop/Notifications.qml b/resources/qml/Governikus/TitleBar/+desktop/Notifications.qml new file mode 100644 index 0000000..b67976a --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+desktop/Notifications.qml @@ -0,0 +1,113 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.NotificationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + + +Item { + id: baseItem + + visible: SettingsModel.showInAppNotifications + + readonly property string icon: { + if (d.unreadMsg) { + if (NotificationModel.lastType === "developermode") return "qrc:///images/desktop/bell_red.svg" + if (NotificationModel.lastType === "feedback") return "qrc:///images/desktop/bell_green.svg" + } + return "qrc:///images/desktop/bell_white.svg" + } + signal newNotification() + function toggle() { + if (!fadingAnimation.running) d.fadeIn = !d.fadeIn + if (d.fadeIn) { + logEntryList.positionViewAtEnd() + d.unreadMsg = false + } + } + + QtObject { + id: d + + property bool fadeIn: false + property bool unreadMsg: false + } + + Connections { + target: NotificationModel + onRowsInserted: { + if (!d.fadeIn) d.unreadMsg = true + baseItem.newNotification() + } + } + + Rectangle { + id: logList + + height: ApplicationModel.scaleFactor * 200 + width: ApplicationModel.scaleFactor * 800 + + anchors.right: parent.right + anchors.rightMargin: -radius + anchors.bottom: parent.bottom + anchors.bottomMargin: d.fadeIn ? radius - height : 0 + + radius: logEntryList.spacing + border.color: Constants.blue + border.width: Math.max(1, ApplicationModel.scaleFactor * 3) + color: Qt.lighter(Constants.blue, 1.15) + + Behavior on anchors.bottomMargin { + PropertyAnimation { + id: fadingAnimation + duration: Constants.animation_duration + easing.type: Easing.InOutQuad + } + } + + MouseArea { + anchors.fill: parent + } + + ListView { + id: logEntryList + + anchors.fill: parent + anchors.margins: spacing + anchors.rightMargin: 2 * spacing + + clip: true + spacing: ApplicationModel.scaleFactor * 10 + model: NotificationModel + delegate: Row { + spacing: logEntryList.spacing + + GText { + id: notificationTime + + text: model.time + } + + GText { + width: logEntryList.width - notificationTime.width - spacing + + text: model.text + textStyle: model.type === "developermode" ? Style.text.normal_warning : Style.text.normal + + onLinkActivated: Qt.openUrlExternally(link) + } + } + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AlwaysOn + } + } + } +} diff --git a/resources/qml/Governikus/TitleBar/+desktop/TitleBar.qml b/resources/qml/Governikus/TitleBar/+desktop/TitleBar.qml index 31332a0..7751d39 100644 --- a/resources/qml/Governikus/TitleBar/+desktop/TitleBar.qml +++ b/resources/qml/Governikus/TitleBar/+desktop/TitleBar.qml @@ -1,21 +1,24 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 Item { id: titleBar - height: actionRow.height + 2 * Constants.titlebar_padding - - focus: true - KeyNavigation.tab: rootAction + height: actionRow.height + 2 * Style.dimens.titlebar_padding Accessible.role: Accessible.Grouping - Accessible.name: qsTr("Titlebar") - Accessible.description: qsTr("This bar represents the navigation tree of the AusweisApp2.") - - property Item navSuccessor: null + Accessible.name: qsTr("Titlebar") + SettingsModel.translationTrigger + Accessible.description: qsTr("This bar represents the navigation tree of the AusweisApp2.") + SettingsModel.translationTrigger + activeFocusOnTab: true property var contentRoot signal rootClicked(); @@ -24,7 +27,6 @@ Item { function updateActions() { actionRow.children = [rootAction] - rootAction.KeyNavigation.tab = rightTitleBarActions.children[0] addRecursive(contentRoot) } @@ -33,19 +35,20 @@ Item { var child = root.children[i] if (child.sectionPageTypeMarker && child.visible) { if (child.titleBarAction) { - addAction(child.titleBarAction) + actionRow.children.push(child.titleBarAction) } addRecursive(child) } } } - function addAction(newAction) { - rightMostAction.KeyNavigation.tab = newAction + Notifications { + id: notifications - actionRow.children.push(newAction) - newAction.customSubAction.KeyNavigation.tab = settingsButton - newAction.KeyNavigation.tab = rightTitleBarActions.children[0] + anchors.right: parent.right + anchors.top: parent.bottom + + onNewNotification: notifyButton.notify() } Rectangle @@ -53,28 +56,33 @@ Item { anchors.fill: parent color: Constants.blue + FocusPoint { + scope: titleBar + } + Row { id: actionRow height: rootAction.height anchors.left: parent.left - anchors.leftMargin: Constants.titlebar_padding + anchors.leftMargin: Style.dimens.titlebar_padding anchors.verticalCenter: parent.verticalCenter - spacing: Constants.titlebar_spacing + spacing: Style.dimens.titlebar_padding readonly property Item lastAction: children && children.length > 0 ? children[children.length - 1] : rootAction TitleBarAction { id: rootAction - showArrow: false - text: qsTr("Start") - onClicked: titleBar.rootClicked() - Component.onCompleted: { - rootAction.customSubAction.KeyNavigation.tab = settingsButton - rootAction.KeyNavigation.tab = rightTitleBarActions.children[0] - } + activeFocusOnTab: true + + showArrow: false + //: LABEL DESKTOP_QML + text: qsTr("Start") + SettingsModel.translationTrigger + enabled: rightMostAction.rootEnabled + + onClicked: titleBar.rootClicked() } } @@ -84,13 +92,14 @@ Item { anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom - anchors.margins: Constants.titlebar_padding - spacing: Constants.titlebar_spacing + anchors.margins: Style.dimens.titlebar_padding + spacing: Style.dimens.titlebar_padding children: [ rightMostAction.customSubAction, settingsButton, - helpButton + helpButton, + notifyButton ] TitleBarButton { @@ -100,8 +109,8 @@ Item { source: "qrc:///images/desktop/settings_icon.svg" visible: rightMostAction.showSettings - KeyNavigation.tab: helpButton - Accessible.name: qsTr("Settings") + activeFocusOnTab: true + Accessible.name: qsTr("Settings") + SettingsModel.translationTrigger onClicked: rightMostAction.settingsHandler() } @@ -110,13 +119,26 @@ Item { id: helpButton height: rightTitleBarActions.height - source: "qrc:///images/desktop/help_icon.svg" + source: "qrc:///images/desktop/info_manual.svg" visible: rightMostAction.showHelp - KeyNavigation.tab: titleBar.navSuccessor - Accessible.name: qsTr("Help") + activeFocusOnTab: true + Accessible.name: qsTr("Help") + SettingsModel.translationTrigger - onClicked: qmlExtension.openOnlineHelp(rightMostAction.helpTopic) + onClicked: ApplicationModel.openOnlineHelp(rightMostAction.helpTopic) + } + + TitleBarButton { + id: notifyButton + + height: rightTitleBarActions.height + source: notifications.icon + visible: SettingsModel.showInAppNotifications + + activeFocusOnTab: true + Accessible.name: qsTr("Notifications") + SettingsModel.translationTrigger + + onClicked: notifications.toggle() } } } diff --git a/resources/qml/Governikus/TitleBar/+desktop/TitleBarAction.qml b/resources/qml/Governikus/TitleBar/+desktop/TitleBarAction.qml index 930b168..26a5cfb 100644 --- a/resources/qml/Governikus/TitleBar/+desktop/TitleBarAction.qml +++ b/resources/qml/Governikus/TitleBar/+desktop/TitleBarAction.qml @@ -1,6 +1,11 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.View 1.0 FocusScope { @@ -16,17 +21,18 @@ FocusScope { property Item customSubAction: Item { visible: false } - property bool showSettings: true + property bool rootEnabled: true + + property bool showSettings: false property var customSettingsHandler - readonly property var settingsHandler: customSettingsHandler ? customSettingsHandler : function() { - // TODO open settings - } + readonly property var settingsHandler: customSettingsHandler ? customSettingsHandler : function() {} property bool showHelp: true property string helpTopic: "applicationPage" Accessible.role: Accessible.Button Accessible.name: text.text + activeFocusOnTab: true Keys.onSpacePressed: scope.clicked() @@ -34,7 +40,7 @@ FocusScope { id: row height: text.height - spacing: Constants.titlebar_spacing + spacing: Style.dimens.titlebar_padding Image { id: arrow @@ -50,6 +56,8 @@ FocusScope { Accessible.role: Accessible.Button Accessible.name: text.text + color: scope.enabled ? Constants.white : Constants.lightgrey + FocusFrame { scope: scope } diff --git a/resources/qml/Governikus/TitleBar/+desktop/TitleBarButton.qml b/resources/qml/Governikus/TitleBar/+desktop/TitleBarButton.qml index 6b15c1a..55b679f 100644 --- a/resources/qml/Governikus/TitleBar/+desktop/TitleBarButton.qml +++ b/resources/qml/Governikus/TitleBar/+desktop/TitleBarButton.qml @@ -1,22 +1,75 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 +import Governikus.Global 1.0 import Governikus.View 1.0 +import Governikus.Type.NotificationModel 1.0 Button { id: button - width: height - enabled: visible + property alias source: image.source + function notify() { + blinkerAnimation.start() + } + + width: height Accessible.role: Accessible.Button - FocusFrame {} - + enabled: visible background: Image { id: image + + height: button.height * (button.down ? 0.9 : 1) + width: height + anchors.centerIn: parent - sourceSize.height: button.height * (button.down ? 0.9 : 1) + + sourceSize.height: height + sourceSize.width: width + + Rectangle { + id: blinker + + anchors.fill: parent + anchors.margins: image.height / -4 + + opacity: 0 + radius: height / 4 + color: NotificationModel.lastType === "developermode" ? Constants.red : Constants.green + + SequentialAnimation { + id: blinkerAnimation + + loops: 3 + + PropertyAnimation { target: blinker; property: "opacity"; from: 0; to: 0.5; duration: 300 } + PropertyAnimation { target: blinker; property: "opacity"; from: 0.5; to: 0; duration: 300 } + } + } + } + + FocusFrame {} + + MouseArea { + id: mouseArea + + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onPressed: mouse.accepted = false + hoverEnabled: true + } + + ToolTip { + visible: mouseArea.containsMouse + + text: parent.Accessible.name + delay: 500 } } diff --git a/resources/qml/Governikus/TitleBar/+desktop/TitleBarText.qml b/resources/qml/Governikus/TitleBar/+desktop/TitleBarText.qml index c062662..9816371 100644 --- a/resources/qml/Governikus/TitleBar/+desktop/TitleBarText.qml +++ b/resources/qml/Governikus/TitleBar/+desktop/TitleBarText.qml @@ -1,9 +1,13 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 GText { - color: Constants.white - font.weight: Font.Bold - font.pixelSize: Constants.titlebar_font_size + textStyle: Style.text.header + font.bold: true } diff --git a/resources/qml/Governikus/TitleBar/+mobile/+android/Hamburger.qml b/resources/qml/Governikus/TitleBar/+mobile/+android/Hamburger.qml index 0009768..f4e0c33 100644 --- a/resources/qml/Governikus/TitleBar/+mobile/+android/Hamburger.qml +++ b/resources/qml/Governikus/TitleBar/+mobile/+android/Hamburger.qml @@ -1,13 +1,22 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import Governikus.Global 1.0 +import Governikus.Type.SettingsModel 1.0 import QtQuick 2.10 Item { id: baseItem - width: height + + implicitWidth: height visible: state !== "hidden" + Accessible.role: Accessible.Button + Accessible.name: qsTr("Show navigation") + SettingsModel.translationTrigger + Item { id: content anchors.centerIn: parent @@ -51,6 +60,13 @@ Item { } states: [ + State { + // While creating this state seems unnecessary because it's also implicitly defined, it's needed to avoid a + // bug where the Animation hangs and overrides the "" state properties with those of the "back" state. This + // can be seen when selecting "Quit Tutorial" from "TutorialReaderMethodNfc" (or any other subview of the + // tutorial) on Android. + name: "" + }, State { id: backState name: "back" @@ -82,6 +98,11 @@ Item { y: content.height / 2 + backState.delta0 - backState.delta1 width: backState.itemArrowWidth } + + PropertyChanges { + target: baseItem + Accessible.name: qsTr("Back") + SettingsModel.translationTrigger + } }, State { @@ -114,6 +135,11 @@ Item { y: content.height - cancelState.delta0 width: cancelState.delta1 } + + PropertyChanges { + target: baseItem + Accessible.name: qsTr("Cancel") + SettingsModel.translationTrigger + } } ] diff --git a/resources/qml/Governikus/TitleBar/+mobile/+android/TitleBar.qml b/resources/qml/Governikus/TitleBar/+mobile/+android/TitleBar.qml index 42ebdfa..73e87ec 100644 --- a/resources/qml/Governikus/TitleBar/+mobile/+android/TitleBar.qml +++ b/resources/qml/Governikus/TitleBar/+mobile/+android/TitleBar.qml @@ -1,131 +1,62 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 -import Governikus.Type.StatusBarUtil 1.0 +import Governikus.Style 1.0 +Item { + id: baseItem -Item -{ - property int duration: 300 - property alias titleBarOpacity: background.opacity - id: titleBar - height: Constants.titlebar_height + property alias titleBarOpacity: titleBar.titleBarOpacity + property NavigationAction navigationAction + property alias title: titleBar.title + property alias rightAction: titleBar.rightAction + property alias subTitleBarAction: titleBar.subTitleBarAction + property alias color: titleBar.color - readonly property TitleBarAction defaultLeftAction: TitleBarAction {} - readonly property TitleBarAction defaultTitle: TitleBarAction {} - readonly property Item defaultRightAction: Item {} + height: titleBar.height - readonly property TitleBarAction activeleftAction: leftAction ? leftAction : defaultLeftAction - readonly property TitleBarAction activeTitleItem: titleItem ? titleItem : defaultTitle - readonly property Item activeRightAction: rightAction ? rightAction : defaultRightAction + BaseTitleBar { + id: titleBar - property var leftAction - property var titleItem - property var rightAction - property var subTitleBarAction - property var color + anchors { + top: parent.top + left: parent.left + right: parent.right + } + titleAlignment: BaseTitleBar.TitleAlignment.Left - Rectangle { - id: background - color: titleBar.color ? titleBar.color : Constants.blue - anchors.top: parent.top - anchors.left: Constants.is_tablet ? hamburgerFrame.right : parent.left - anchors.bottom: parent.bottom - anchors.right: parent.right + leftAction: Hamburger { + id: burger + height: parent.height + state: navBar.isOpen ? "back" : (baseItem.navigationAction ? baseItem.navigationAction.state : "") + enabled: baseItem.navigationAction ? baseItem.navigationAction.enabled : true - onColorChanged: StatusBarUtil.setStatusBarColor(String(color)) - Behavior on color { ColorAnimation { duration: titleBar.duration } } - } - - Rectangle { - id: hamburgerFrame - height: parent.height - width: Constants.menubar_width - anchors.left: parent.left - color: background.color - opacity: Constants.is_tablet ? 1 : 0 - } - - Hamburger { - id: burger - anchors.horizontalCenter: hamburgerFrame.horizontalCenter - height: parent.height - state: navBar.isOpen ? "back" : activeleftAction.state - - MouseArea { - anchors.fill: parent - onClicked: { - switch (burger.state) { - case "": - navBar.open() - break - case "back": - if (navBar.isOpen) { - navBar.close() - } else { - activeleftAction.clicked(mouse) - } - break - case "hidden": - break - default: - activeleftAction.clicked(mouse) + MouseArea { + anchors.fill: parent + onClicked: { + switch (burger.state) { + case "": + navBar.open() + break + case "back": + if (navBar.isOpen) { + navBar.close() + } else { + baseItem.navigationAction.clicked() + } + break + case "hidden": + break + default: + baseItem.navigationAction.clicked() + } } } } } - - Item { - id: titleText - property string text: activeTitleItem.text - property bool bold: activeTitleItem.font.bold - - anchors.left: burger.right - anchors.leftMargin: Constants.titlebar_spacing - anchors.right: parent.right - anchors.rightMargin: Constants.titlebar_padding + (rightActionStack.width > 0 ? rightAction.width + Constants.titlebar_spacing : 0) - height: parent.height - clip: true - - TitleBarText { - id: oldTitle - width: parent.width - text: parent.text - Component.onCompleted: font.bold = parent.bold - opacity: 0 - - Behavior on text { - SequentialAnimation { - PropertyAnimation { target: oldTitle; property: "opacity"; from: 1; to: 0; duration: titleBar.duration } - PropertyAction { target: oldTitle; property: "font.bold"; value: titleText.bold } - PropertyAction { target: oldTitle; property: "text" } - } - } - } - - TitleBarText { - id: newTitle - width: parent.width - text: parent.text - font.bold: parent.bold - - Behavior on text { - ParallelAnimation { - PropertyAnimation { target: newTitle; property: "opacity"; from: 0; to: 1; duration: titleBar.duration } - PropertyAnimation { target: newTitle; property: "x"; from: width; to: 0; duration: titleBar.duration } - } - } - } - } - - Item { - id: rightActionStack - anchors.rightMargin: Constants.titlebar_padding - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: parent.bottom - width: rightAction ? activeRightAction.contentWidth : 0 - data: activeRightAction - } } diff --git a/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBar.qml b/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBar.qml index 691b08a..f3ffbb8 100644 --- a/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBar.qml +++ b/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBar.qml @@ -1,113 +1,39 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 -import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 Item { - property int duration: 300 - property alias titleBarOpacity: background.opacity - id: titleBar - height: Constants.titlebar_height + activeSubTitleBarAction.height + id: baseItem - readonly property var defaultLeftAction: Text {} - readonly property var defaultTitle: Text {} - readonly property var defaultRightAction: Text {} - readonly property var defaultSubTitleBarAction: Item {} + property alias titleBarOpacity: titleBar.titleBarOpacity + property NavigationAction navigationAction + property alias title: titleBar.title + property alias rightAction: titleBar.rightAction + property alias subTitleBarAction: titleBar.subTitleBarAction + property alias color: titleBar.color - property var activeleftAction: leftAction ? leftAction : defaultLeftAction - property var activeTitleItem: titleItem ? titleItem : defaultTitle - property var activeRightAction: rightAction ? rightAction : defaultRightAction - property var activeSubTitleBarAction: subTitleBarAction ? subTitleBarAction : defaultSubTitleBarAction + height: titleBar.height - property var leftAction - property var titleItem - property var rightAction - property var subTitleBarAction - property var color + BaseTitleBar { + id: titleBar - - Rectangle { - id: background - anchors.fill: parent - color: titleBar.color ? titleBar.color : Constants.blue - - Behavior on color { ColorAnimation { duration: titleBar.duration } } - } - - Item { - id: firstLine - - height: Constants.titlebar_height - width: parent.width - - Item { - id: leftActionStack - data: activeleftAction - width: activeleftAction.width - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: Constants.titlebar_padding + anchors { + top: parent.top + left: parent.left + right: parent.right } - Item { - id: titleText - property string text: activeTitleItem.text - property bool bold: activeTitleItem.font.bold - - anchors.left: parent.left - anchors.leftMargin: leftActionStack.width > 0 ? leftAction.width + Constants.titlebar_spacing : 0 - anchors.right: parent.right - anchors.rightMargin: rightActionStack.width > 0 ? rightAction.width + Constants.titlebar_spacing : 0 - height: parent.height - clip: true - - TitleBarText { - id: oldTitle - text: parent.text - width: parent.width - horizontalAlignment: Text.AlignHCenter - Component.onCompleted: font.bold = parent.bold - opacity: 0 - - Behavior on text { - SequentialAnimation { - PropertyAnimation { target: oldTitle; property: "opacity"; from: 1; to: 0; duration: titleBar.duration } - PropertyAction { target: oldTitle; property: "font.bold"; value: titleText.bold } - PropertyAction { target: oldTitle; property: "text" } - } - } - } - - TitleBarText { - id: newTitle - text: parent.text - width: parent.width - font.bold: parent.bold - horizontalAlignment: Text.AlignHCenter - - Behavior on text { - ParallelAnimation { - PropertyAnimation { target: newTitle; property: "opacity"; from: 0; to: 1; duration: titleBar.duration } - PropertyAnimation { target: newTitle; property: "x"; from: width; to: 0; duration: titleBar.duration } - } - } - } + leftAction: TitleBarNavigation { + state: baseItem.navigationAction ? baseItem.navigationAction.state : "" + enabled: baseItem.navigationAction ? baseItem.navigationAction.enabled : true + onClicked: baseItem.navigationAction.clicked() + onTextUpdated: titleBar.updateLeftAction() } - Item { - id: rightActionStack - data: activeRightAction - width: activeRightAction.width - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: Constants.titlebar_padding - } - } - - Item { - data: activeSubTitleBarAction - width: parent.width - anchors.top: firstLine.bottom - anchors.horizontalCenter: parent.horizontalCenter } } diff --git a/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBarNavigation.qml b/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBarNavigation.qml new file mode 100644 index 0000000..0bf87f7 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+mobile/+ios/TitleBarNavigation.qml @@ -0,0 +1,27 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtGraphicalEffects 1.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + +TitleBarAction { + id: baseItem + + Accessible.role: Accessible.Button + Accessible.name: text + + icon: state === "back" ? "qrc:///images/arrowLeft.svg" : null + //: LABEL ANDROID IOS + text: (state === "cancel" ? qsTr("Cancel") : + //: LABEL ANDROID IOS + state === "edit" ? qsTr("Edit") : + //: LABEL ANDROID IOS + state === "back" ? (qsTr("Back")) : + state === "hidden" ? "" : + "") + SettingsModel.translationTrigger +} diff --git a/resources/qml/Governikus/TitleBar/+mobile/BaseTitleBar.qml b/resources/qml/Governikus/TitleBar/+mobile/BaseTitleBar.qml new file mode 100644 index 0000000..89afdf4 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+mobile/BaseTitleBar.qml @@ -0,0 +1,233 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + +Item { + id: titleBar + + enum TitleAlignment { + Center = 0, + Left + } + + property alias titleBarOpacity: background.opacity + + property alias title: titleText.text + property var leftAction + property var rightAction + property var subTitleBarAction + + property var color + property int titleAlignment: BaseTitleBar.TitleAlignment.Center + property var topSafeAreaMargin: plugin.safeAreaMargins.top + + height: contentLayout.height + topSafeAreaMargin + + function updateLeftAction() { + if (leftActionStack.activeActionItem && typeof leftActionStack.activeActionItem.iconOnly !== "undefined") { + leftActionStack.activeActionItem.iconOnly = titleText.implicitWidth > titleText.implicitAvailableWidth + } + } + + Rectangle { + id: safeAreaBackground + + height: topSafeAreaMargin + anchors { + top: parent.top + left: parent.left + right: parent.right + } + + property color baseColor: titleBar.color ? titleBar.color : Style.color.accent + color: Constants.is_layout_android ? Qt.darker(baseColor, 1.2) : baseColor + + Behavior on color { ColorAnimation { duration: Constants.animation_duration } } + } + + Rectangle { + id: background + + anchors { + top: safeAreaBackground.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + + color: titleBar.color ? titleBar.color : Style.color.accent + + Behavior on color { ColorAnimation { duration: Constants.animation_duration } } + } + + Column { + id: contentLayout + + anchors { + bottom: parent.bottom + right: parent.right + left: parent.left + } + + Item { + id: firstLine + + height: Style.dimens.titlebar_height + width: parent.width + + Item { + id: leftActionStack + + property var actionItem: leftAction + property var activeActionItem + + width: activeActionItem ? activeActionItem.width : 0 + implicitWidth: activeActionItem ? activeActionItem.implicitWidth : 0 + anchors { + top: parent.top + left: parent.left + bottom: parent.bottom + leftMargin: Style.dimens.titlebar_padding + } + + children: activeActionItem ? [activeActionItem] : [] + + onActionItemChanged: leftActionStackAnimateOut.start() + onActiveActionItemChanged: updateLeftAction() + + PropertyAnimation { + id: leftActionStackAnimateOut + target: leftActionStack + property: "opacity" + to: 0 + duration: Constants.animation_duration + easing.type: Easing.InCubic + onStopped: { leftActionStack.activeActionItem = leftActionStack.actionItem; leftActionStackAnimateIn.start() } + } + PropertyAnimation { + id: leftActionStackAnimateIn + target: leftActionStack + property: "opacity" + to: 1 + duration: Constants.animation_duration + easing.type: Easing.OutCubic + } + } + + GText { + id: titleText + + readonly property var centerX: (parent.width / 2) - (width / 2) + readonly property var leftX: leftActionStack.width + leftActionPadding + (1 + (leftActionStack.activeActionItem ? 1 : 0)) * Style.dimens.titlebar_padding + + readonly property var leftActionPadding: leftAction.iconOnly ? Style.dimens.titlebar_padding : 0 + readonly property var completePadding: (2 + (leftActionStack.activeActionItem ? 1 : 0) + (rightActionStack.activeActionItem ? 1 : 0)) * Style.dimens.titlebar_padding + + readonly property var implicitAvailableWidth: parent.width - leftActionStack.implicitWidth - rightActionStack.implicitWidth - completePadding + readonly property var availableWidth: parent.width - leftActionStack.width - rightActionStack.width - completePadding - leftActionPadding + + height: Style.dimens.titlebar_height + width: Math.min(implicitWidth, availableWidth) + anchors { + top: parent.top + bottom: parent.bottom + } + x: titleAlignment === BaseTitleBar.TitleAlignment.Center ? Math.max(leftX, centerX) : leftX + + verticalAlignment: Text.AlignVCenter + maximumLineCount: 1 + elide: Text.ElideRight + wrapMode: Text.NoWrap + font.bold: true + textStyle: Style.text.header_inverse + + onTextChanged: updateLeftAction() + + Behavior on text { + SequentialAnimation { + PropertyAnimation { + target: titleText + property: "opacity" + to: 0 + duration: Constants.animation_duration + easing.type: Easing.InCubic + } + PropertyAction { + target: titleText + property: "text" + } + PropertyAnimation { + target: titleText + property: "opacity" + to: 1 + duration: Constants.animation_duration + easing.type: Easing.OutCubic + } + } + } + + Behavior on x { + NumberAnimation { + from: parent.width * 0.75 + duration: Constants.animation_duration + easing.type: Easing.OutQuart + } + } + } + + Item { + id: rightActionStack + + property var actionItem: rightAction + property var activeActionItem + + width: activeActionItem ? activeActionItem.width : 0 + implicitWidth: activeActionItem ? activeActionItem.implicitWidth : 0 + anchors { + top: parent.top + right: parent.right + bottom: parent.bottom + rightMargin: Style.dimens.titlebar_padding + } + + children: activeActionItem ? [activeActionItem] : [] + + onActionItemChanged: rightActionStackAnimateOut.start() + onActiveActionItemChanged: rightActionStackAnimateIn.start() + + PropertyAnimation { + id: rightActionStackAnimateOut + target: rightActionStack + property: "opacity" + to: 0 + duration: Constants.animation_duration + easing.type: Easing.InCubic + onStopped: rightActionStack.activeActionItem = rightActionStack.actionItem + } + PropertyAnimation { + id: rightActionStackAnimateIn + target: rightActionStack + property: "opacity" + to: 1 + duration: Constants.animation_duration + easing.type: Easing.OutCubic + } + } + } + + Item { + id: secondLine + + width: parent.width + height: subTitleBarAction ? subTitleBarAction.height : 0 + + data: subTitleBarAction ? subTitleBarAction : [] + } + } +} diff --git a/resources/qml/Governikus/TitleBar/+mobile/NavigationAction.qml b/resources/qml/Governikus/TitleBar/+mobile/NavigationAction.qml new file mode 100644 index 0000000..f863fc0 --- /dev/null +++ b/resources/qml/Governikus/TitleBar/+mobile/NavigationAction.qml @@ -0,0 +1,10 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +Item { + property string state + signal clicked +} diff --git a/resources/qml/Governikus/TitleBar/+mobile/TitleBarAction.qml b/resources/qml/Governikus/TitleBar/+mobile/TitleBarAction.qml index 8810bb2..db18925 100644 --- a/resources/qml/Governikus/TitleBar/+mobile/TitleBarAction.qml +++ b/resources/qml/Governikus/TitleBar/+mobile/TitleBarAction.qml @@ -1,24 +1,145 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtGraphicalEffects 1.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 -MouseArea { - property string text: "" - property alias font: titleBarText.font +Item { + id: baseItem - height: Constants.titlebar_height - width: titleBarText.width - anchors.centerIn: parent + property string icon + property string text + property bool iconOnly: false + signal clicked + signal textUpdated - TitleBarText { - id: titleBarText - anchors.centerIn: parent + height: Style.dimens.titlebar_height + implicitWidth: imageItem.implicitWidth + textItem.implicitWidth + width: imageItem.implicitWidth + (iconOnly ? 0 : textItem.implicitWidth) - text: ( parent.text !== "" ? parent.text : - parent.state === "cancel" ? qsTr("Cancel") : - parent.state === "edit" ? qsTr("Edit") : - parent.state === "back" ? qsTr("< back") : - parent.state === "hidden" ? "" : - "") + settingsModel.translationTrigger + Accessible.role: Accessible.Button + Accessible.name: text + Accessible.onPressAction: if (Qt.platform.os === "ios") clicked() + + Image { + id: imageItem + + sourceSize.width: source ? 18 : 0 + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + + fillMode: Image.PreserveAspectFit + source: icon + + Behavior on source { + SequentialAnimation { + PropertyAnimation { + target: imageItem + property: "opacity" + to: 0 + duration: Constants.animation_duration + easing.type: Easing.InCubic + } + PropertyAction { + target: imageItem + property: "source" + } + PropertyAnimation { + target: imageItem + property: "opacity" + to: 1 + duration: Constants.animation_duration + easing.type: Easing.OutCubic + } + } + } + + ColorOverlay { + anchors.fill: imageItem + source: imageItem + color: Style.color.primary_text_inverse + } + } + + GText { + id: textItem + + visible: !iconOnly + anchors { + left: imageItem.right + verticalCenter: parent.verticalCenter + } + + Accessible.ignored: true + + verticalAlignment: Text.AlignVCenter + maximumLineCount: 1 + elide: Text.ElideRight + wrapMode: Text.NoWrap + textStyle: Style.text.normal_inverse + text: baseItem.text + + onTextChanged: baseItem.textUpdated() + + onVisibleChanged: { + if (visible) { + fadeIn.start() + } else { + fadeOut.start() + } + } + + PropertyAnimation { + id: fadeOut + target: textItem + property: "opacity" + to: 0 + duration: Constants.animation_duration + easing.type: Easing.InCubic + } + PropertyAnimation { + id: fadeIn + target: textItem + property: "opacity" + to: 1 + duration: Constants.animation_duration + easing.type: Easing.OutCubic + } + + Behavior on text { + SequentialAnimation { + PropertyAnimation { + target: textItem + property: "opacity" + to: 0 + duration: Constants.animation_duration + easing.type: Easing.InCubic + } + PropertyAction { + target: textItem + property: "text" + } + PropertyAnimation { + target: textItem + property: "opacity" + to: 1 + duration: Constants.animation_duration + easing.type: Easing.OutCubic + } + } + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: baseItem.clicked() } } diff --git a/resources/qml/Governikus/TitleBar/+mobile/TitleBarText.qml b/resources/qml/Governikus/TitleBar/+mobile/TitleBarText.qml deleted file mode 100644 index 51147f5..0000000 --- a/resources/qml/Governikus/TitleBar/+mobile/TitleBarText.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Text { - id: textItem - anchors.verticalCenter: parent.verticalCenter - color: "white" - font.pixelSize: Constants.titlebar_font_size - maximumLineCount: 1 - elide: Text.ElideRight -} diff --git a/resources/qml/Governikus/TitleBar/qmldir b/resources/qml/Governikus/TitleBar/qmldir index 6db473e..a42013a 100644 --- a/resources/qml/Governikus/TitleBar/qmldir +++ b/resources/qml/Governikus/TitleBar/qmldir @@ -1,9 +1,13 @@ module TitleBar +internal BaseTitleBar BaseTitleBar.qml +internal Hamburger Hamburger.qml internal TitleBarText TitleBarText.qml +internal TitleBarNavigation TitleBarNavigation.qml CancelAction 1.0 CancelAction.qml -Hamburger 1.0 Hamburger.qml +NavigationAction 1.0 NavigationAction.qml +Notifications 1.0 Notifications.qml TitleBar 1.0 TitleBar.qml TitleBarAction 1.0 TitleBarAction.qml TitleBarButton 1.0 TitleBarButton.qml diff --git a/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantBinaryDecisionView.qml b/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantBinaryDecisionView.qml new file mode 100644 index 0000000..e6b2b32 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantBinaryDecisionView.qml @@ -0,0 +1,123 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + +SectionPage { + id: baseItem + + property alias questionText: mainTextElement.text + property alias questionSubText: subTextElement.text + property string mainIconSource: "qrc:///images/status_info.svg" + property string agreeText: "" + property string disagreeText: "" + property bool addRingAroundIcon: false + + signal agree() + signal disagree() + + activeFocusOnTab: false + + Image { + visible: !baseItem.addRingAroundIcon + height: Style.dimens.status_icon_large + width: height + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.top + anchors.verticalCenterOffset: baseItem.height / 4 + + source: baseItem.mainIconSource + } + + StatusIcon { + visible: baseItem.addRingAroundIcon + height: Style.dimens.status_icon_large + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.top + anchors.verticalCenterOffset: baseItem.height / 4 + + source: baseItem.mainIconSource + } + + GText { + id: mainTextElement + + visible: mainTextElement.text !== "" + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.verticalCenter + + Accessible.role: Accessible.Heading + Accessible.name: text + activeFocusOnTab: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + textStyle: Style.text.header + + FocusFrame {} + } + + GText { + id: subTextElement + + visible: subTextElement.text !== "" + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainTextElement.bottom + anchors.topMargin: Constants.text_spacing + + Accessible.role: Accessible.Heading + Accessible.name: text + activeFocusOnTab: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + textStyle: Style.text.normal + + FocusFrame {} + } + + SetupAssistantButton { + id: disagreeButton + + width: ApplicationModel.scaleFactor * 120 + anchors.margins: Constants.component_spacing + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenterOffset: -baseItem.width / 5 + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + subText: qsTr("No") + SettingsModel.translationTrigger + buttonImage: "qrc:///images/cancel.svg" + onClicked: baseItem.disagree() + } + + SetupAssistantButton { + id: agreeButton + + width: ApplicationModel.scaleFactor * 120 + anchors.margins: Constants.component_spacing + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenterOffset: baseItem.width / 5 + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + subText: qsTr("Yes") + SettingsModel.translationTrigger + buttonImage: "qrc:///images/check.svg" + onClicked: baseItem.agree() + } +} diff --git a/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantButton.qml b/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantButton.qml new file mode 100644 index 0000000..d917209 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantButton.qml @@ -0,0 +1,72 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + +Button { + id: baseItem + + property string subText: "" + property alias buttonImage: image.source + + height: icon.height + Constants.component_spacing + buttonText.height + + Accessible.role: Accessible.Button + Accessible.name: subText + + background: Item { + id: content + + anchors.fill: parent + + Rectangle { + id: icon + + width: baseItem.width + height: baseItem.width + anchors.horizontalCenter: parent.horizontalCenter + anchors.margins: baseItem.down ? baseItem.width / 32 : 0 + anchors.top: parent.top + + radius: baseItem.width / 2 + border.width: baseItem.width / 40; + border.color: Constants.white + color: Style.color.transparent + + Rectangle { + anchors.fill: parent + anchors.margins: parent.height / 8; + + radius: height / 2 + color: Qt.lighter(Constants.blue, 1.1) + + Image { + id: image + anchors.centerIn: parent + + sourceSize.height: parent.height / 2; + } + } + } + + GText { + id: buttonText + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + + text: baseItem.subText + textStyle: Style.text.header + } + } + + FocusFrame {} +} + diff --git a/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantCardReaderView.qml b/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantCardReaderView.qml new file mode 100644 index 0000000..6a91931 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantCardReaderView.qml @@ -0,0 +1,34 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 + +SectionPage { + id: baseItem + + signal advanceSetupAssistant() + + activeFocusOnTab: false + + SetupAssistantButton { + id: disagreeButton + + width: ApplicationModel.scaleFactor * 120 + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.margins: Constants.component_spacing + + activeFocusOnTab: true + + //: LABEL DESKTOP_QML + subText: qsTr("Advance") + SettingsModel.translationTrigger + buttonImage: "qrc:///images/desktop/continue_arrow.svg" + onClicked: baseItem.advanceSetupAssistant() + } +} diff --git a/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantView.qml b/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantView.qml new file mode 100644 index 0000000..6043024 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+desktop/SetupAssistantView.qml @@ -0,0 +1,206 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.ResultView 1.0 +import Governikus.TitleBar 1.0 +import Governikus.View 1.0 +import Governikus.SettingsView 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.ChangePinModel 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.NumberModel 1.0 + +SectionPage { + id: baseItem + + enum SubViews { + Welcome = 0, + HistorySetting, + CardReader, + TransportPin, + Finished + } + + activeFocusOnTab: false + + isAbstract: true + onVisibleChanged: if (visible) d.reset() + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Setup Assistant") + SettingsModel.translationTrigger + rootEnabled: d.allowNavigation + showSettings: false + helpTopic: "setupAssistant" + onClicked: d.reset() + } + + QtObject { + id: d + + property int activeView: SetupAssistantView.SubViews.Welcome + readonly property bool allowNavigation: !SettingsModel.showSetupAssistantOnStart + + function reset() { + d.activeView = SetupAssistantView.SubViews.Welcome + } + } + + ResultView { + visible: d.activeView === SetupAssistantView.SubViews.Welcome + + resultType: ResultView.Type.IsInfo + //: INFO DESKTOP_QML Welcome message when starting the setup assistant. + text: qsTr("Welcome to the AusweisApp2. Please take a few moments to setup the environment to your needs. Every decision you make can later be changed in the settings menu.") + SettingsModel.translationTrigger + + onNextView: d.activeView = SetupAssistantView.SubViews.HistorySetting + } + + SetupAssistantBinaryDecisionView { + visible: d.activeView === SetupAssistantView.SubViews.HistorySetting + + mainIconSource: "qrc:///images/desktop/main_history.svg" + //: INFO DESKTOP_QML Question if the authentication history shall be stored. + questionText: qsTr("Do you want to save a history of performed authentications?") + SettingsModel.translationTrigger + //: INFO DESKTOP_QML Information text which data is stored in the history record. + questionSubText: qsTr("The following data is saved: authentication date, service provider contact data, read data.") + SettingsModel.translationTrigger + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("History Setting") + SettingsModel.translationTrigger + rootEnabled: d.allowNavigation + showSettings: false + helpTopic: "setupAssistant" + } + + onAgree: { + SettingsModel.historyEnabled = true + d.activeView = SetupAssistantView.SubViews.CardReader + } + + onDisagree: { + SettingsModel.historyEnabled = false + d.activeView = SetupAssistantView.SubViews.CardReader + } + } + + TabbedReaderView { + id: readerView + + visible: d.activeView === SetupAssistantView.SubViews.CardReader + + rootEnabled: d.allowNavigation + + onCloseView: d.activeView = SetupAssistantView.SubViews.HistorySetting + + Button { + id: button + + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.rightMargin: Constants.component_spacing + anchors.bottomMargin: Constants.component_spacing + width: icon.width + ApplicationModel.scaleFactor * 10 + buttonText.width + height: icon.height + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Continue") + SettingsModel.translationTrigger + + activeFocusOnTab: true + onClicked: d.activeView = SetupAssistantView.SubViews.TransportPin + + background: Item { + id: content + + readonly property int imageWidth: ApplicationModel.scaleFactor * 60 + + anchors.fill: parent + + GText { + id: buttonText + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + + //: LABEL DESKTOP_QML + text: qsTr("Continue") + SettingsModel.translationTrigger + textStyle: Style.text.normal + } + + Rectangle { + id: icon + + width: content.imageWidth + height: content.imageWidth + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + + radius: button.width / 2 + border.width: content.imageWidth / 40 + border.color: Constants.white + color: Style.color.transparent + + Rectangle { + anchors.fill: parent + anchors.margins: parent.height / 8 + + radius: height / 2 + color: Qt.lighter(Constants.blue, 1.1) + + Image { + id: image + anchors.centerIn: parent + source: "qrc:///images/desktop/continue_arrow.svg" + + sourceSize.height: parent.height / 2 + } + } + } + } + + FocusFrame {} + } + } + + SetupAssistantBinaryDecisionView { + visible: d.activeView === SetupAssistantView.SubViews.TransportPin + + mainIconSource: "qrc:///images/reader/default_reader.png" + //: INFO DESKTOP_QML Inquiry message if the 5 digit transport PIN should be changed to an ordinary PIN (now). + questionText: qsTr("Do you want to change your transport PIN to a personal PIN now?") + SettingsModel.translationTrigger + //: INFO DESKTOP_QML Hint that this change may be carried out form the main menu as well and that it is required to use the online authentication feature of the id card. + questionSubText: qsTr("This process can always be started from the main menu. The online-ID function is only usable with a personal PIN.") + SettingsModel.translationTrigger + addRingAroundIcon: true + + titleBarAction: TitleBarAction { + //: LABEL DESKTOP_QML + text: qsTr("Transport PIN") + SettingsModel.translationTrigger + rootEnabled: d.allowNavigation + showSettings: false + helpTopic: "setupAssistant" + } + + onAgree: { + NumberModel.requestTransportPin = true + ChangePinModel.startWorkflow() + } + onDisagree: d.activeView = SetupAssistantView.SubViews.Finished + } + + ResultView { + visible: d.activeView === SetupAssistantView.SubViews.Finished + + resultType: ResultView.Type.IsSuccess + //: INFO DESKTOP_QML Success message after completing the setup assistant. + text: qsTr("You have completed the setup of the AusweisApp2 successfully.") + SettingsModel.translationTrigger + onNextView: { + SettingsModel.showSetupAssistantOnStart = false + baseItem.nextView(pName) + } + } + +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialCollapseAnimation.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialCollapseAnimation.qml index d56db49..1bd1520 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialCollapseAnimation.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialCollapseAnimation.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 SequentialAnimation { diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialContent.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialContent.qml index 2c2ccc1..d4c4d59 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialContent.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialContent.qml @@ -1,6 +1,11 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 Rectangle { id: contentBackground @@ -19,6 +24,6 @@ Rectangle { anchors.top: parent.top topPadding: parent.width * 0.15 bottomPadding: Constants.component_spacing - spacing: Constants.tutorial_component_spacing + spacing: Style.dimens.tutorial_component_spacing } } diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialExpandAnimation.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialExpandAnimation.qml index 5d85c53..2206176 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialExpandAnimation.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialExpandAnimation.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 SequentialAnimation { diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialFooter.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialFooter.qml index 164b7fd..af3ca23 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialFooter.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialFooter.qml @@ -1,10 +1,22 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 +import QtGraphicalEffects 1.0 -Rectangle { + +Item { id: baseItem - height: Math.max(backToMenu.height, quitTutorial.height) + + property alias color: colorOverlay.color + property alias shaderSource: effectSource.sourceItem + + height: plugin.safeAreaMargins.bottom + Math.max(backToMenu.height, quitTutorial.height) property alias backRotation: leftArrow.rotation property alias backText: menuText.text @@ -13,6 +25,7 @@ Rectangle { signal menuClicked() signal quitTutorialClicked() + state: "showOnlyQuit" states: [ State { name: "showBothOptions"; when: baseItem.backToMenuActive PropertyChanges { target: backToMenu; opacity: 1 } @@ -31,14 +44,46 @@ Rectangle { AnchorAnimation { duration: 500; easing.type: Easing.InOutQuad } } ] + onVisibleChanged: if (visible) { + // When the user quits the tutorial on iOS while we're in the "showBothOptions" state, the + // animation will finish when the MoreView is already active. This results in quitTutorial + // being wrongly positioned the next time the users enters the tutorial. The following lines + // work around this issue: + if (state === "showOnlyQuit") { + quitTutorial.anchors.horizontalCenter = baseItem.horizontalCenter + } + } + + ShaderEffectSource { + id: effectSource + + anchors.fill: parent + sourceRect: Qt.rect(baseItem.x, baseItem.y, baseItem.width, baseItem.height) + } + FastBlur { + anchors.fill: effectSource + source: effectSource + radius: 32 + } + ColorOverlay { + id: colorOverlay + + anchors.fill: parent + color: footer.color + Behavior on color { ColorAnimation { duration: Constants.animation_duration } } + opacity: 0.7 + } Item { id: backToMenu anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter + anchors.top: parent.top height: menuRow.height + 2 * Constants.component_spacing width: menuRow.width + Accessible.name: menuText.text + Accessible.onPressAction: if (Qt.platform.os === "ios") baseItem.menuClicked() + MouseArea { anchors.fill: parent preventStealing: true @@ -64,13 +109,16 @@ Rectangle { fillMode: Image.PreserveAspectFit } - Text { + GText { id: menuText + anchors.verticalCenter: parent.verticalCenter - text: qsTr("Fold in") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size - color: Constants.white + + Accessible.ignored: true + + //: LABEL ANDROID IOS + text: qsTr("Fold in") + SettingsModel.translationTrigger + textStyle: Style.text.normal_inverse } } } @@ -79,7 +127,10 @@ Rectangle { id: quitTutorial height: quitRow.height + 2 * Constants.component_spacing width: quitRow.width - anchors.verticalCenter: parent.verticalCenter + anchors.top: parent.top + + Accessible.name: quitText.text + Accessible.onPressAction: if (Qt.platform.os === "ios") baseItem.quitTutorialClicked() MouseArea { anchors.fill: parent @@ -97,13 +148,16 @@ Rectangle { padding: Constants.component_spacing spacing: Constants.component_spacing - Text { + GText { id: quitText + anchors.verticalCenter: parent.verticalCenter - text: qsTr("Quit tutorial") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size - color: Constants.white + + Accessible.ignored: true + + //: LABEL ANDROID IOS + text: qsTr("Quit tutorial") + SettingsModel.translationTrigger + textStyle: Style.text.normal_inverse } Image { diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialHeader.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialHeader.qml index 73f9ad1..6eb5bdb 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialHeader.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialHeader.qml @@ -1,7 +1,12 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 import Governikus.Global 1.0 +import Governikus.Style 1.0 Item { id: baseItem @@ -17,6 +22,9 @@ Item { property real initY signal clicked() + Accessible.name: title.text + Accessible.onPressAction: if (Qt.platform.os === "ios") clicked() + Image{ id: headerImage width: parent.width @@ -39,18 +47,22 @@ Item { } } - Text { + GText { id: title + anchors.horizontalCenter: parent.horizontalCenter y: ((categoryAbove ? 0.575 : 0.5) * parent.height) - (0.5 * height) + + Accessible.ignored: true + font.bold: true - font.pixelSize: Constants.tutorial_header_font_size + textStyle: Style.text.tutorial_title layer.enabled: true layer.effect: DropShadow { - verticalOffset: Utils.dp(3) - horizontalOffset: Utils.dp(3) + verticalOffset: 3 + horizontalOffset: 3 color: Constants.white - radius: Utils.dp(1) + radius: 1 samples: 3 } } diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialHow.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialHow.qml index f03491a..449b8ae 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialHow.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialHow.qml @@ -1,6 +1,13 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + TutorialContent { id: baseItem @@ -8,39 +15,51 @@ TutorialContent { signal firePush(var pSectionPage) signal quitTutorialClicked() - TutorialReaderMethodNfc { - id: readerMethodNfc - visible: false - onQuitTutorialClicked: baseItem.quitTutorialClicked() + Item { + Component { + id: readerMethodNfc + + TutorialReaderMethodNfc { + onQuitTutorialClicked: baseItem.quitTutorialClicked() + } + } + + Component { + id: readerMethodSacMobile + + TutorialReaderMethodSacMobile { + onQuitTutorialClicked: baseItem.quitTutorialClicked() + } + } + + Component { + id: readerMethodSacDesktop + + TutorialReaderMethodSacDesktop { + onQuitTutorialClicked: baseItem.quitTutorialClicked() + } + } + + Component { + id: readerMethodBluetooth + + TutorialReaderMethodBluetooth { + onQuitTutorialClicked: baseItem.quitTutorialClicked() + } + } } - TutorialReaderMethodSacMobile { - id: readerMethodSacMobile - visible: false - onQuitTutorialClicked: baseItem.quitTutorialClicked() - } - TutorialReaderMethodSacDesktop { - id: readerMethodSacDesktop - visible: false - onQuitTutorialClicked: baseItem.quitTutorialClicked() - } - - TutorialReaderMethodBluetooth { - id: readerMethodBluetooth - visible: false - onQuitTutorialClicked: baseItem.quitTutorialClicked() - } - - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("How can I use the AusweisApp2 on my smartphone?") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: INFO ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("How can I use the AusweisApp2 on my iPhone?") + : qsTr("How can I use the AusweisApp2 on my smartphone?")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.italic: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } TutorialSeperator { @@ -76,38 +95,39 @@ TutorialContent { centerX: 0.2 } - Text { + GText { width: parent.width * 0.6 - text: qsTr("Many Android devices can access the id card via the NFC interface.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: INFO ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("Many iPhones (iPhone 7 and newer) can access the id card via the built-in NFC interface.") + : qsTr("Many Android devices can access the id card via the built-in NFC interface.")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) } } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("You can find a list of compatible NFC-capable smartphones here:") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + visible: Constants.is_layout_android + //: LABEL ANDROID IOS + text: qsTr("You can find a list of compatible NFC-capable smartphones here:") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: "%1".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + visible: Constants.is_layout_android + //: LABEL ANDROID IOS + text: "%1".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap onLinkActivated: Qt.openUrlExternally(link) } @@ -119,15 +139,14 @@ TutorialContent { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("The AusweisApp2 offers the following options to access your id card:") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("The AusweisApp2 offers the following options to access your id card:") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.italic: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } @@ -140,8 +159,8 @@ TutorialContent { width: parent.width * 0.95 height: methodNfcSection.height color: Constants.white - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how MouseArea { anchors.fill: parent @@ -152,17 +171,16 @@ TutorialContent { id: methodNfcSection width: parent.width spacing: Constants.component_spacing - padding: Utils.dp(10) + padding: 10 - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Direct connection via NFC chip") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("Direct connection via NFC chip") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Rectangle { @@ -170,15 +188,14 @@ TutorialContent { height: radius * 2 width: radius * 2 radius: numberOne.height - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how - Text { + GText { id: numberOne anchors.centerIn: parent text: "1" - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -192,14 +209,15 @@ TutorialContent { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.6 - text: qsTr("App on Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("App on iPhone with NFC chip as card reader") + : qsTr("App on Android smartphone with NFC chip as card reader")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { @@ -221,8 +239,8 @@ TutorialContent { width: parent.width * 0.95 height: methodSacDesktopSection.height color: Constants.white - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how MouseArea { anchors.fill: parent @@ -233,17 +251,16 @@ TutorialContent { id: methodSacDesktopSection width: parent.width spacing: Constants.component_spacing - padding: Utils.dp(10) + padding: 10 - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Smartphone as card reader") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("Smartphone as card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Rectangle { @@ -251,15 +268,14 @@ TutorialContent { height: radius * 2 width: radius * 2 radius: numberTwo.height - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how - Text { + GText { id: numberTwo anchors.centerIn: parent text: "2" - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -274,8 +290,8 @@ TutorialContent { height: radius * 2 width: radius * 2 radius: parent.width * 0.06 - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how y: (parent.height * 0.4) - (height / 2) x: (parent.width * 0.5) - (width / 2) @@ -298,21 +314,23 @@ TutorialContent { centerY: 0.5 } - Text { + GText { width: parent.width * 0.4 - text: qsTr("App on computer without NFC chip") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("App on computer without NFC chip") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.25) - (width / 2) y: (parent.height * 0.95) - (height / 2) } - Text { + GText { width: parent.width * 0.4 - text: qsTr("Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Smartphone with NFC chip as card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.95) - (height / 2) @@ -338,8 +356,8 @@ TutorialContent { width: parent.width * 0.95 height: methodSacMobileSection.height color: Constants.white - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how MouseArea { anchors.fill: parent @@ -350,22 +368,21 @@ TutorialContent { id: methodSacMobileSection width: parent.width spacing: Constants.component_spacing - padding: Utils.dp(10) + padding: 10 Rectangle { anchors.horizontalCenter: parent.horizontalCenter height: radius * 2 width: radius * 2 radius: numberTwo.height - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how - Text { + GText { id: numberThree anchors.centerIn: parent text: "3" - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -380,8 +397,8 @@ TutorialContent { height: radius * 2 width: radius * 2 radius: parent.width * 0.06 - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how y: (parent.height * 0.4) - (height / 2) x: (parent.width * 0.5) - (width / 2) @@ -404,21 +421,23 @@ TutorialContent { centerY: 0.5 } - Text { + GText { width: parent.width * 0.4 - text: qsTr("App on tablet or smartphone without NFC chip") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("App on tablet or smartphone without NFC chip") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.25) - (width / 2) y: (parent.height * 0.95) - (height / 2) } - Text { + GText { width: parent.width * 0.4 - text: qsTr("Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Smartphone with NFC chip as card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.95) - (height / 2) @@ -444,8 +463,8 @@ TutorialContent { width: parent.width * 0.95 height: methodBluetoothSection.height color: Constants.white - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how MouseArea { anchors.fill: parent @@ -456,17 +475,16 @@ TutorialContent { id: methodBluetoothSection width: parent.width spacing: Constants.component_spacing - padding: Utils.dp(10) + padding: 10 - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Using a bluetooth card reader") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("Using a bluetooth card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Rectangle { @@ -474,15 +492,14 @@ TutorialContent { height: radius * 2 width: radius * 2 radius: numberFour.height - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how - Text { + GText { id: numberFour anchors.centerIn: parent text: "4" - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -497,8 +514,8 @@ TutorialContent { height: radius * 2 width: radius * 2 radius: parent.width * 0.06 - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how y: (parent.height * 0.4) - (height / 2) x: (parent.width * 0.5) - (width / 2) @@ -521,21 +538,23 @@ TutorialContent { centerY: 0.5 } - Text { + GText { width: parent.width * 0.4 - text: qsTr("App on smartphone or tablet") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("App on smartphone or tablet") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.25) - (width / 2) y: (parent.height * 0.95) - (height / 2) } - Text { + GText { width: parent.width * 0.4 - text: qsTr("Bluetooth card reader") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Bluetooth card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.95) - (height / 2) @@ -556,18 +575,17 @@ TutorialContent { property alias text: textContent.text height: textContent.height + 2 * Constants.component_spacing width: parent.width - color: Constants.tutorial_blue + color: Style.color.tutorial_how - Text { + GText { id: textContent anchors.centerIn: parent color: Constants.white width: parent.width * 0.8 - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - text: qsTr("Another tip") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Another tip") + SettingsModel.translationTrigger } } @@ -576,14 +594,13 @@ TutorialContent { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("For lenghty forms, e.g. a BAf\u00F6G application, we recommend you to use the AusweisApp2 on a computer...") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("For lenghty forms, e.g. a BAf\u00F6G application, we recommend you to use the AusweisApp2 on a computer...") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Item { @@ -599,26 +616,26 @@ TutorialContent { centerY: 0.5 } - Text { + GText { width: parent.width * 0.5 - text: qsTr("Filling long forms is no fun on a smartphone!") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Filling long forms is no fun on a smartphone!") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap - color: Constants.tutorial_blue + color: Style.color.accent x: parent.width * 0.5 y: (parent.height * 0.5) - (height / 2) } } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("... and to use a smartphone to communicate with your ID card. A USB reader is of course also an alternative.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("... and to use a smartphone to communicate with your ID card. A USB reader is of course also an alternative.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialImage.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialImage.qml index 72b9a38..e1067fc 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialImage.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialImage.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 @@ -11,4 +15,6 @@ Image { y: (parent.height * centerY) - (height / 2) x: (parent.width * centerX) - (width / 2) + + asynchronous: true } diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialImportant.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialImportant.qml index 7a05388..3d65695 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialImportant.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialImportant.qml @@ -1,20 +1,28 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.NumberModel 1.0 import Governikus.Type.ChangePinModel 1.0 + TutorialContent { id: baseItem - Text { + signal letsGoClicked() + + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: (settingsModel.language === "en" ? qsTr("Please exchange your") : qsTr("Before you use the online ID function please change the")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("Please exchange your") : qsTr("Before you use the online ID function please change the")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } TutorialSeperator { @@ -25,25 +33,23 @@ TutorialContent { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("5 digits long") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("5 digits long") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("transport PIN") + settingsModel.translationTrigger - font.family: "Noto Serif" + //: LABEL ANDROID IOS + text: qsTr("transport PIN") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true - font.pixelSize: Constants.tutorial_content_header_h1_font_size horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } TutorialImage { @@ -60,25 +66,23 @@ TutorialContent { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("with a personal") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("with a personal") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("6 digits long PIN") + settingsModel.translationTrigger - font.family: "Noto Serif" + //: LABEL ANDROID IOS + text: qsTr("6 digits long PIN") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true - font.pixelSize: Constants.tutorial_content_header_h1_font_size horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } TutorialImage { @@ -86,24 +90,22 @@ TutorialContent { source: "qrc:///images/tutorial/generated/important_pin6.svg" } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: (settingsModel.language === "en" ? qsTr("before you use the online ID function!") : qsTr("change!")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("before you use the online ID function!") : qsTr("change!")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("The transport PIN is send to you by the Bundesdruckerei via mail.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("The transport PIN is sent to you by the Bundesdruckerei via mail.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } @@ -115,27 +117,25 @@ TutorialContent { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Choose for this purpose the menu entry PIN management") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("Choose for this purpose the menu entry PIN management") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Item { height: parent.width * 0.6 width: parent.width - Text { + GText { width: parent.width * 0.35 - text: qsTr("Later you can also change your personal PIN here") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("Later you can also change your personal PIN here") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.2) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -143,7 +143,8 @@ TutorialContent { TutorialImage { id: screenshot - source: qsTr("qrc:///images/tutorial/screenshot_pin_management_menu_en.png") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/screenshot_pin_management_menu_%1_en.png").arg(Constants.layout) + SettingsModel.translationTrigger z: 3 readonly property real rightX: x + width @@ -176,20 +177,20 @@ TutorialContent { } } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("... or click this button to change your PIN right now:") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("... or click this button to change your PIN right now:") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } GButton { anchors.horizontalCenter: parent.horizontalCenter anchors.margins: Constants.component_spacing - text: qsTr("Change PIN") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Change PIN") + SettingsModel.translationTrigger onClicked: { NumberModel.requestTransportPin = true ChangePinModel.startWorkflow() @@ -219,20 +220,24 @@ TutorialContent { } } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Learn more about this in the YouTube video") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("Learn more about this in the YouTube video") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Item { width: parent.width height: letsGoImage.height + MouseArea { + anchors.fill: parent + onClicked: baseItem.letsGoClicked() + } + TutorialImage { id: letsGoImage width: parent.width @@ -242,13 +247,12 @@ TutorialContent { centerY: 0.5 } - Text { + GText { width: parent.width - text: qsTr("Let's go") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Let's go") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + textStyle: Style.text.tutorial_header color: Constants.white x: (parent.width * 0.5) - (width / 2) @@ -257,14 +261,13 @@ TutorialContent { } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Do you still have questions?") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Do you still have questions?") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { @@ -276,24 +279,22 @@ TutorialContent { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("You can read our FAQs or write to us...") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("You can read our FAQs or write to us...") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: "%1
%2
%3".arg(qsTr("www.ausweisapp.bund.de")).arg(qsTr("or")).arg(qsTr("www.personalausweisportal.de")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: "%1
%2
%3".arg(qsTr("www.ausweisapp.bund.de")).arg(qsTr("or")).arg(qsTr("www.personalausweisportal.de")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.Wrap onLinkActivated: Qt.openUrlExternally(link) } } @@ -302,13 +303,14 @@ TutorialContent { source: "qrc:///images/tutorial/section_seperator_important.svg" } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("You can always access this tutorial again from the side bar.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("You can always access this tutorial again from the \"More\" section in the menu bar.") + : qsTr("You can always access this tutorial again from the side bar.")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodBluetooth.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodBluetooth.qml index 39eac98..7b15578 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodBluetooth.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodBluetooth.qml @@ -1,13 +1,24 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Tutorial: Bluetooth") + settingsModel.translationTrigger; font.bold: true } + + titleBarVisible: false + automaticSafeAreaMarginHandling: false + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + //: LABEL ANDROID IOS + title: qsTr("Tutorial: Bluetooth") + SettingsModel.translationTrigger signal quitTutorialClicked() @@ -28,20 +39,24 @@ SectionPage { height: content.contentHeight anchors.horizontalCenter: parent.horizontalCenter + Item { + id: statusBarSpacer + width: parent.width + height: statusBar.height + } Column { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Using a bluetooth card reader") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("Using a bluetooth card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Rectangle { @@ -49,15 +64,14 @@ SectionPage { height: radius * 2 width: radius * 2 radius: numberFour.height - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how - Text { + GText { id: numberFour anchors.centerIn: parent text: "4" - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -72,8 +86,8 @@ SectionPage { height: radius * 2 width: radius * 2 radius: parent.width * 0.06 - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how y: (parent.height * 0.4) - (height / 2) x: (parent.width * 0.5) - (width / 2) @@ -96,21 +110,23 @@ SectionPage { centerY: 0.5 } - Text { + GText { width: parent.width * 0.4 - text: qsTr("App on smartphone or tablet") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("App on smartphone or tablet") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.25) - (width / 2) y: (parent.height * 0.95) - (height / 2) } - Text { + GText { width: parent.width * 0.4 - text: qsTr("Bluetooth card reader") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Bluetooth card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.95) - (height / 2) @@ -132,14 +148,13 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: qsTr("You need a suitable card reader if you want to use the bluetooth connection.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("You need a suitable card reader if you want to use the bluetooth connection.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h1_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header } Image { @@ -154,28 +169,26 @@ SectionPage { width: parent.width height: Math.max(leftText.height, rightText.height) - Text { + GText { id: leftText width: parent.width * 0.4 anchors.left: parent.left - leftPadding: Utils.dp(30) - text: qsTr("Set the card reader visible first...") + settingsModel.translationTrigger + leftPadding: 30 + //: LABEL ANDROID IOS + text: qsTr("Set the card reader visible first...") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary } - Text { + GText { id: rightText width: parent.width * 0.4 anchors.right: parent.right - rightPadding: Utils.dp(30) - text: qsTr("... and then pair it with your device.") + settingsModel.translationTrigger + rightPadding: 30 + //: LABEL ANDROID IOS + text: qsTr("... and then pair it with your device.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary } } } @@ -195,14 +208,13 @@ SectionPage { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.6 - text: qsTr("Click the link on the website of the service provider.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Click the link on the website of the service provider.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary } } @@ -224,34 +236,32 @@ SectionPage { TutorialImage { id: userdataExample - source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + settingsModel.translationTrigger + source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + SettingsModel.translationTrigger width: parent.width * 0.8 centerX: 0.5 centerY: 0.75 } - Text { + GText { id: textOpenAutomatic width: parent.width * 0.6 - text: qsTr("The App opens automatically.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("The App opens automatically.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_content x: (parent.width * 0.7) - (width / 2) y: (parent.height * 0) - (height / 2) } - Text { + GText { id: textAccessWhoWhat width: parent.width * 0.6 - text: qsTr("The AusweisApp2 will display who wants to access which data.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("The AusweisApp2 will display who wants to access which data.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary x: (parent.width * 0.7) - (width / 2) y: (parent.height * 0.2) - (height / 2) @@ -266,12 +276,12 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { id: startProcessText anchors.horizontalCenter: parent.horizontalCenter - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size - text: qsTr("Start the process with a click on:") + settingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary + //: LABEL ANDROID IOS + text: qsTr("Start the process with a click on:") + SettingsModel.translationTrigger } Row { @@ -290,7 +300,8 @@ SectionPage { GButton { id: identifyButton iconSource: "qrc:///images/npa.svg" - text: qsTr("Identify now") + settingsModel.translationTrigger; + //: LABEL ANDROID IOS + text: qsTr("Proceed to PIN entry") + SettingsModel.translationTrigger; animationsDisabled: true } } @@ -310,7 +321,7 @@ SectionPage { TutorialImage { id: screenshotIdentify - source: qsTr("qrc:///images/tutorial/screenshot_choose_reader_en.png") + settingsModel.translationTrigger + source: qsTr("qrc:///images/tutorial/screenshot_choose_reader_%1_en.png").arg(Constants.layout) + SettingsModel.translationTrigger width: parent.width * 0.5 centerX: 0.5 @@ -326,14 +337,13 @@ SectionPage { } } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Tap on Bluetooth") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Tap on Bluetooth") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h1_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header } } @@ -346,14 +356,13 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Insert card into card reader") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Insert card into card reader") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h1_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header } Image { @@ -364,14 +373,13 @@ SectionPage { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("... and confirm the displayed information.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("... and confirm the displayed information.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h1_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header } } @@ -387,13 +395,12 @@ SectionPage { width: parent.width height: pin6Image.height + Constants.component_spacing * 2 - Text { + GText { width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.05) - (height / 2) @@ -409,28 +416,26 @@ SectionPage { centerX: 0.5 } - Text { + GText { width: parent.width - text: qsTr("6 digits long PIN") + settingsModel.translationTrigger - font.family: "Noto Serif" + //: LABEL ANDROID IOS + text: qsTr("6 digits long PIN") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true - font.pixelSize: Constants.tutorial_content_header_h1_font_size horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 2 x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.2) - (height / 2) } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("now on the card reader!") : qsTr("enter on the card reader!")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("now on the card reader!") : qsTr("enter on the card reader!")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.9) - (height / 2) @@ -450,15 +455,14 @@ SectionPage { centerX: 0.2 } - Text { + GText { id: noticeText width: parent.width * 0.6 - text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -494,10 +498,17 @@ SectionPage { } } + TutorialStatusBar { + id: statusBar + + shaderSource: sectionPageFlickable + } + TutorialReaderMethodFooter { id: footer width: baseItem.width + shaderSource: sectionPageFlickable onMenuClicked: firePop() onQuitTutorialClicked: { firePop() diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodFooter.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodFooter.qml index 8281218..02facab 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodFooter.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodFooter.qml @@ -1,14 +1,23 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + TutorialFooter { id: footer width: baseItem.width anchors.horizontalCenter: parent.horizontalCenter - color: Constants.tutorial_blue + color: Style.color.accent anchors.bottom: parent.bottom backRotation: 180 - backText: qsTr("Back") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + backText: qsTr("Back") + SettingsModel.translationTrigger + state: "showBothOptions" } diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodNfc.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodNfc.qml index 20c9617..f6a46ee 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodNfc.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodNfc.qml @@ -1,13 +1,24 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Tutorial: NFC") + settingsModel.translationTrigger; font.bold: true } + + titleBarVisible: false + automaticSafeAreaMarginHandling: false + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + //: LABEL ANDROID IOS + title: qsTr("Tutorial: NFC") + SettingsModel.translationTrigger signal quitTutorialClicked() @@ -28,19 +39,24 @@ SectionPage { height: content.contentHeight anchors.horizontalCenter: parent.horizontalCenter + Item { + id: statusBarSpacer + width: parent.width + height: statusBar.height + } + Column { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Direct connection via NFC chip") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("Direct connection via NFC chip") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Rectangle { @@ -48,15 +64,14 @@ SectionPage { height: radius * 2 width: radius * 2 radius: numberOne.height - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how - Text { + GText { id: numberOne anchors.centerIn: parent text: "1" - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -70,14 +85,15 @@ SectionPage { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.6 - text: qsTr("App on Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("App on iPhone with NFC chip as card reader") + : qsTr("App on Android smartphone with NFC chip as card reader")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } @@ -100,13 +116,12 @@ SectionPage { width: parent.width } - Text { + GText { width: parent.width * 0.6 - text: qsTr("Click link on the website of the service provider.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Click link on the website of the service provider.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.9) - (height / 2) @@ -131,34 +146,32 @@ SectionPage { TutorialImage { id: userdataExample - source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + settingsModel.translationTrigger + source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + SettingsModel.translationTrigger width: parent.width * 0.8 centerX: 0.5 centerY: 0.75 } - Text { + GText { id: textOpenAutomatic width: parent.width * 0.6 - text: qsTr("The App opens automatically.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("The App opens automatically.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_content x: (parent.width * 0.7) - (width / 2) y: (parent.height * 0) - (height / 2) } - Text { + GText { id: textAccessWhoWhat width: parent.width * 0.6 - text: qsTr("The AusweisApp2 will display who wants to access which data.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("The AusweisApp2 will display who wants to access which data.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary x: (parent.width * 0.7) - (width / 2) y: (parent.height * 0.2) - (height / 2) @@ -173,12 +186,12 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { id: startProcessText anchors.horizontalCenter: parent.horizontalCenter - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size - text: qsTr("Start the process with a click on:") + settingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary + //: LABEL ANDROID IOS + text: qsTr("Start the process with a click on:") + SettingsModel.translationTrigger } Row { @@ -197,7 +210,8 @@ SectionPage { GButton { id: identifyButton iconSource: "qrc:///images/npa.svg" - text: qsTr("Identify now") + settingsModel.translationTrigger; + //: LABEL ANDROID IOS + text: qsTr("Proceed to PIN entry") + SettingsModel.translationTrigger; animationsDisabled: true } } @@ -220,14 +234,15 @@ SectionPage { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("... and place the id card flat onto the NFC interface.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("... and place the top of the iPhone onto the id card.") + : qsTr("... and place the id card flat onto the NFC interface.")) + + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary } Item { @@ -243,14 +258,13 @@ SectionPage { centerX: 0.2 } - Text { + GText { width: parent.width * 0.6 - text: qsTr("Do not move device or id card!") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("Do not move device or id card!") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -259,12 +273,14 @@ SectionPage { } TutorialSeperator { + visible: Constants.is_layout_android source: "qrc:///images/tutorial/section_seperator_how.svg" } Item { width: parent.width height: nfcPosition.height + 2 * Constants.component_spacing + visible: Constants.is_layout_android TutorialImage { id: nfcPosition @@ -275,28 +291,26 @@ SectionPage { centerX: 0.35 } - Text { + GText { width: parent.width * 0.5 - text: qsTr("The correct position is specific for your device...") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("The correct position is specific for your device...") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.4) - (height / 2) } - Text { + GText { width: parent.width * 0.5 - text: "%2".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")).arg(qsTr("To mobile devices")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: "%2".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")).arg(qsTr("To mobile devices")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary font.bold: true font.underline: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap onLinkActivated: Qt.openUrlExternally(link) x: (parent.width * 0.75) - (width / 2) @@ -316,13 +330,12 @@ SectionPage { width: parent.width height: pin6Image.height + Constants.component_spacing * 2 - Text { + GText { width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.05) - (height / 2) @@ -338,28 +351,26 @@ SectionPage { centerX: 0.5 } - Text { + GText { width: parent.width - text: qsTr("6 digits long PIN") + settingsModel.translationTrigger - font.family: "Noto Serif" + //: LABEL ANDROID IOS + text: qsTr("6 digits long PIN") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true - font.pixelSize: Constants.tutorial_content_header_h1_font_size horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 2 x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.2) - (height / 2) } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.9) - (height / 2) @@ -379,15 +390,14 @@ SectionPage { centerX: 0.2 } - Text { + GText { id: noticeText2 width: parent.width * 0.6 - text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -423,10 +433,17 @@ SectionPage { } } + TutorialStatusBar { + id: statusBar + + shaderSource: sectionPageFlickable + } + TutorialReaderMethodFooter { id: footer width: baseItem.width + shaderSource: sectionPageFlickable onMenuClicked: firePop() onQuitTutorialClicked: { firePop() diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacDesktop.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacDesktop.qml index b36905c..39ab98a 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacDesktop.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacDesktop.qml @@ -1,13 +1,24 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Tutorial: Smartphone as card reader") + settingsModel.translationTrigger; font.bold: true } + + titleBarVisible: false + automaticSafeAreaMarginHandling: false + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + //: LABEL ANDROID IOS + title: qsTr("Tutorial: Smartphone as card reader") + SettingsModel.translationTrigger signal quitTutorialClicked() @@ -28,19 +39,24 @@ SectionPage { height: content.contentHeight anchors.horizontalCenter: parent.horizontalCenter + Item { + id: statusBarSpacer + width: parent.width + height: statusBar.height + } + Column { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Smartphone as card reader") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("Smartphone as card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Rectangle { @@ -48,15 +64,14 @@ SectionPage { height: radius * 2 width: radius * 2 radius: numberTwo.height - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how - Text { + GText { id: numberTwo anchors.centerIn: parent text: "2" - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -71,8 +86,8 @@ SectionPage { height: radius * 2 width: radius * 2 radius: parent.width * 0.06 - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how y: (parent.height * 0.4) - (height / 2) x: (parent.width * 0.5) - (width / 2) @@ -95,21 +110,23 @@ SectionPage { centerY: 0.5 } - Text { + GText { width: parent.width * 0.4 - text: qsTr("App on computer without NFC chip") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("App on computer without NFC chip") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.25) - (width / 2) y: (parent.height * 0.95) - (height / 2) } - Text { + GText { width: parent.width * 0.4 - text: qsTr("Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Smartphone with NFC chip as card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.95) - (height / 2) @@ -130,14 +147,13 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Install AusweisApp2 on both your computer and your android smartphone with NFC ability.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Install AusweisApp2 on both your computer and your smartphone with NFC capability.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { @@ -165,15 +181,14 @@ SectionPage { source: "qrc:///images/tutorial/hint.svg" } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Both devices have to be connected to the same wifi network") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Both devices have to be connected to the same wifi network") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { @@ -193,18 +208,20 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Now enter \"Smartphone as card reader\" in the app on your android smartphone...") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("Now choose \"Remote\" in the AusweisApp2 on your smartphone...") + : qsTr("Now choose \"Smartphone as card reader\" in the AusweisApp2 on your smartphone...")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { - source: qsTr("qrc:///images/tutorial/generated/reader_sac_menu_en.svg") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/generated/reader_sac_menu_%1_en.svg").arg(Constants.layout) + SettingsModel.translationTrigger anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 height: width * (sourceSize.height / sourceSize.width) @@ -220,20 +237,20 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Now") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Now") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignRight - wrapMode: Text.WordWrap } GButton { id: remoteButton - buttonColor: "green" + buttonColor: Constants.green anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Start remote service") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Start remote service") + SettingsModel.translationTrigger animationsDisabled: true } @@ -246,19 +263,19 @@ SectionPage { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Next") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Next") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignRight - wrapMode: Text.WordWrap } GButton { id: pairingButton anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Start pairing") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Start pairing") + SettingsModel.translationTrigger animationsDisabled: true } @@ -279,34 +296,32 @@ SectionPage { Rectangle { id: greyBackgroundRect anchors.horizontalCenter: parent.horizontalCenter - width: pairingCodeText.width - Utils.dp(40) + width: pairingCodeText.width - 40 height: width - color: Constants.tutorial_very_light_grey + color: Style.color.tutorial_box_background } - Text { + GText { id: pairingCodeText anchors.horizontalCenter: parent.horizontalCenter anchors.top: greyBackgroundRect.top - topPadding: Utils.dp(30) - text: qsTr("Pairing code") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + topPadding: 30 + //: LABEL ANDROID IOS + text: qsTr("Pairing code") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { id: appearingText anchors.bottom: greyBackgroundRect.bottom - bottomPadding: Utils.dp(30) + bottomPadding: 30 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("appears!") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("appears!") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } } @@ -327,24 +342,22 @@ SectionPage { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: qsTr("Start the App now on your computer and enter the settings.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Start the App now on your computer and enter the settings.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: qsTr("Select the Card Readers tab.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Select the Card Readers tab.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } @@ -362,20 +375,20 @@ SectionPage { TutorialImage { id: desktopPairing - source: qsTr("qrc:///images/tutorial/screenshot_pairing_en.png") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/screenshot_pairing_en.png") + SettingsModel.translationTrigger width: parent.width * 0.6 } } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: qsTr("Select smartphone from list and click \"pair\"") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Select smartphone from list and click \"pair\"") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Item { @@ -391,14 +404,13 @@ SectionPage { centerX: 0.2 } - Text { + GText { width: parent.width * 0.6 - text: qsTr("Enter pairing code next.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("Enter pairing code next.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -421,14 +433,13 @@ SectionPage { width: parent.width * 0.5 } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.6 - text: qsTr("Click link on the website of the service provider.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Click link on the website of the service provider.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary } } @@ -450,34 +461,33 @@ SectionPage { TutorialImage { id: userdataExample - source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + SettingsModel.translationTrigger width: parent.width * 0.8 centerX: 0.5 centerY: 0.75 } - Text { + GText { id: textOpenAutomatic width: parent.width * 0.6 - text: qsTr("The App opens automatically.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("The App opens automatically.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_content x: (parent.width * 0.7) - (width / 2) y: (parent.height * 0) - (height / 2) } - Text { + GText { id: textAccessWhoWhat width: parent.width * 0.6 - text: qsTr("The AusweisApp2 will display who wants to access which data.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("The AusweisApp2 will display who wants to access which data.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary x: (parent.width * 0.7) - (width / 2) y: (parent.height * 0.2) - (height / 2) @@ -492,12 +502,12 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { id: startProcessText anchors.horizontalCenter: parent.horizontalCenter - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size - text: qsTr("Start the process with a click on:") + settingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary + //: LABEL ANDROID IOS + text: qsTr("Start the process with a click on:") + SettingsModel.translationTrigger } Row { @@ -516,7 +526,8 @@ SectionPage { GButton { id: identifyButton iconSource: "qrc:///images/npa.svg" - text: qsTr("Identify now") + settingsModel.translationTrigger; + //: LABEL ANDROID IOS + text: qsTr("Proceed to PIN entry") + SettingsModel.translationTrigger; animationsDisabled: true } } @@ -532,20 +543,20 @@ SectionPage { Image { anchors.horizontalCenter: parent.horizontalCenter - source: qsTr( "qrc:///images/tutorial/generated/where_lay_down_id.svg") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr( "qrc:///images/tutorial/generated/where_lay_down_id.svg") + SettingsModel.translationTrigger width: parent.width * 0.8 height: width * (sourceSize.height / sourceSize.width) fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("... and place the id card flat onto the NFC interface.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("... and place the id card onto the NFC interface.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary } Item { @@ -561,14 +572,13 @@ SectionPage { centerX: 0.2 } - Text { + GText { width: parent.width * 0.6 - text: qsTr("Do not move device or id card!") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("Do not move device or id card!") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -593,28 +603,26 @@ SectionPage { centerX: 0.35 } - Text { + GText { width: parent.width * 0.5 - text: qsTr("The correct position is specific for your device...") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("The correct position is specific for your device...") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.4) - (height / 2) } - Text { + GText { width: parent.width * 0.5 - text: "%2".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")).arg(qsTr("To mobile devices")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: "%2".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")).arg(qsTr("To mobile devices")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary font.bold: true font.underline: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap onLinkActivated: Qt.openUrlExternally(link) x: (parent.width * 0.75) - (width / 2) @@ -634,13 +642,12 @@ SectionPage { width: parent.width height: pin6Image.height + Constants.component_spacing * 2 - Text { + GText { width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.05) - (height / 2) @@ -656,28 +663,26 @@ SectionPage { centerX: 0.5 } - Text { + GText { width: parent.width - text: qsTr("6 digits long PIN") + settingsModel.translationTrigger - font.family: "Noto Serif" + //: LABEL ANDROID IOS + text: qsTr("6 digits long PIN") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true - font.pixelSize: Constants.tutorial_content_header_h1_font_size horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 2 x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.2) - (height / 2) } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.9) - (height / 2) @@ -697,15 +702,14 @@ SectionPage { centerX: 0.2 } - Text { + GText { id: noticeText2 width: parent.width * 0.6 - text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -741,10 +745,17 @@ SectionPage { } } + TutorialStatusBar { + id: statusBar + + shaderSource: sectionPageFlickable + } + TutorialReaderMethodFooter { id: footer width: baseItem.width + shaderSource: sectionPageFlickable onMenuClicked: firePop() onQuitTutorialClicked: { firePop() diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacMobile.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacMobile.qml index 7183fbd..5c2f621 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacMobile.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialReaderMethodSacMobile.qml @@ -1,13 +1,24 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: baseItem - leftTitleBarAction: TitleBarAction { state: "back"; onClicked: firePop() } - headerTitleBarAction: TitleBarAction { text: qsTr("Tutorial: Smartphone as card reader") + settingsModel.translationTrigger; font.bold: true } + + titleBarVisible: false + automaticSafeAreaMarginHandling: false + navigationAction: NavigationAction { state: "back"; onClicked: firePop() } + //: LABEL ANDROID IOS + title: qsTr("Tutorial: Smartphone as card reader") + SettingsModel.translationTrigger signal quitTutorialClicked() @@ -28,6 +39,12 @@ SectionPage { height: content.contentHeight anchors.horizontalCenter: parent.horizontalCenter + Item { + id: statusBarSpacer + width: parent.width + height: statusBar.height + } + Column { width: parent.width spacing: Constants.component_spacing @@ -37,15 +54,14 @@ SectionPage { height: radius * 2 width: radius * 2 radius: numberTwo.height - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how - Text { + GText { id: numberThree anchors.centerIn: parent text: "3" - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + textStyle: Style.text.tutorial_header_secondary font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -60,8 +76,8 @@ SectionPage { height: radius * 2 width: radius * 2 radius: parent.width * 0.06 - border.width: Utils.dp(3) - border.color: Constants.tutorial_blue + border.width: 3 + border.color: Style.color.tutorial_how y: (parent.height * 0.4) - (height / 2) x: (parent.width * 0.5) - (width / 2) @@ -84,21 +100,23 @@ SectionPage { centerY: 0.5 } - Text { + GText { width: parent.width * 0.4 - text: qsTr("App on tablet or smartphone without NFC chip") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("App on tablet or smartphone without NFC chip") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.25) - (width / 2) y: (parent.height * 0.95) - (height / 2) } - Text { + GText { width: parent.width * 0.4 - text: qsTr("Android smartphone with NFC chip as card reader") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Smartphone with NFC chip as card reader") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.95) - (height / 2) @@ -119,14 +137,13 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Install AusweisApp2 on both your device without NFC and your android smartphone with NFC ability.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Install AusweisApp2 on both your device without NFC and your smartphone with NFC capability.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { @@ -154,15 +171,14 @@ SectionPage { source: "qrc:///images/tutorial/hint.svg" } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Both devices have to be connected to the same wifi network") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Both devices have to be connected to the same wifi network") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { @@ -187,18 +203,20 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Now enter \"Smartphone as card reader\" in the app on your android smartphone with...") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("Now choose \"Remote\" in the AusweisApp2 on your smartphone...") + : qsTr("Now choose \"Smartphone as card reader\" in the AusweisApp2 on your smartphone...")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { - source: qsTr("qrc:///images/tutorial/generated/reader_sac_menu_en.svg") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/generated/reader_sac_menu_%1_en.svg").arg(Constants.layout) + SettingsModel.translationTrigger anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 height: width * (sourceSize.height / sourceSize.width) @@ -214,20 +232,20 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Now") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Now") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignRight - wrapMode: Text.WordWrap } GButton { id: remoteButton - buttonColor: "green" + buttonColor: Constants.green anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Start remote service") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Start remote service") + SettingsModel.translationTrigger animationsDisabled: true } @@ -240,19 +258,19 @@ SectionPage { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Next") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Next") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignRight - wrapMode: Text.WordWrap } GButton { id: pairingButton anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Start pairing") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Start pairing") + SettingsModel.translationTrigger animationsDisabled: true } @@ -273,34 +291,32 @@ SectionPage { Rectangle { id: greyBackgroundRect anchors.horizontalCenter: parent.horizontalCenter - width: pairingCodeText.width - Utils.dp(40) + width: pairingCodeText.width - 40 height: width - color: Constants.tutorial_very_light_grey + color: Style.color.tutorial_box_background } - Text { + GText { id: pairingCodeText anchors.horizontalCenter: parent.horizontalCenter anchors.top: greyBackgroundRect.top - topPadding: Utils.dp(30) - text: qsTr("Pairing code") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + topPadding: 30 + //: LABEL ANDROID IOS + text: qsTr("Pairing code") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { id: appearingText anchors.bottom: greyBackgroundRect.bottom - bottomPadding: Utils.dp(30) + bottomPadding: 30 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("appears!") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("appears!") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } } @@ -326,24 +342,24 @@ SectionPage { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Now open the app on your device without NFC and select Smartphone as card reader.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("Now open the AusweisApp2 on your device without NFC and select Configure remote service.") + : qsTr("Now open the AusweisApp2 on your device without NFC and select Smartphone as card reader.")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Now select Settings.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Now select Settings.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } @@ -363,14 +379,13 @@ SectionPage { fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.7 - text: qsTr("Choose smartphone from list") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Choose smartphone from list") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Item { @@ -386,14 +401,13 @@ SectionPage { centerX: 0.2 } - Text { + GText { width: parent.width * 0.6 - text: qsTr("Enter pairing code next.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("Enter pairing code next.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -421,14 +435,13 @@ SectionPage { width: parent.width * 0.5 } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.6 - text: qsTr("Click link on the website of the service provider on the device without NFC.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Click link on the website of the service provider on the device without NFC.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary } } @@ -450,34 +463,33 @@ SectionPage { TutorialImage { id: userdataExample - source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg") + SettingsModel.translationTrigger width: parent.width * 0.8 centerX: 0.5 centerY: 0.75 } - Text { + GText { id: textOpenAutomatic width: parent.width * 0.6 - text: qsTr("The App opens automatically.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("The App opens automatically.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_content x: (parent.width * 0.7) - (width / 2) y: (parent.height * 0) - (height / 2) } - Text { + GText { id: textAccessWhoWhat width: parent.width * 0.6 - text: qsTr("The AusweisApp2 will display who wants to access which data.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("The AusweisApp2 will display who wants to access which data.") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary x: (parent.width * 0.7) - (width / 2) y: (parent.height * 0.2) - (height / 2) @@ -492,12 +504,12 @@ SectionPage { width: parent.width spacing: Constants.component_spacing - Text { + GText { id: startProcessText anchors.horizontalCenter: parent.horizontalCenter - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size - text: qsTr("Start the process with a click on:") + settingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary + //: LABEL ANDROID IOS + text: qsTr("Start the process with a click on:") + SettingsModel.translationTrigger } Row { @@ -516,7 +528,8 @@ SectionPage { GButton { id: identifyButton iconSource: "qrc:///images/npa.svg" - text: qsTr("Identify now") + settingsModel.translationTrigger; + //: LABEL ANDROID IOS + text: qsTr("Proceed to PIN entry") + SettingsModel.translationTrigger; animationsDisabled: true } } @@ -536,7 +549,8 @@ SectionPage { TutorialImage { id: screenshotIdentify - source: qsTr("qrc:///images/tutorial/screenshot_choose_reader_en.png") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/screenshot_choose_reader_%1_en.png").arg(Constants.layout) + SettingsModel.translationTrigger width: parent.width * 0.5 centerX: 0.5 @@ -552,14 +566,13 @@ SectionPage { } } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Tap on Wifi") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Tap on Wifi") + SettingsModel.translationTrigger horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h1_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header } } @@ -573,20 +586,21 @@ SectionPage { Image { anchors.horizontalCenter: parent.horizontalCenter - source: qsTr( "qrc:///images/tutorial/generated/where_lay_down_id.svg") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr( "qrc:///images/tutorial/generated/where_lay_down_id.svg") + SettingsModel.translationTrigger width: parent.width * 0.8 height: width * (sourceSize.height / sourceSize.width) fillMode: Image.PreserveAspectFit } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("... and place the id card flat onto the NFC interface.") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("... and place the id card onto the NFC interface.") + SettingsModel.translationTrigger + horizontalAlignment: Text.AlignHCenter - font.pixelSize: Constants.tutorial_content_header_h2_font_size - font.family: "Noto Serif" - wrapMode: Text.WordWrap + textStyle: Style.text.tutorial_header_secondary } Item { @@ -602,14 +616,13 @@ SectionPage { centerX: 0.2 } - Text { + GText { width: parent.width * 0.6 - text: qsTr("Do not move device or id card!") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("Do not move device or id card!") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -634,28 +647,26 @@ SectionPage { centerX: 0.35 } - Text { + GText { width: parent.width * 0.5 - text: qsTr("The correct position is specific for your device...") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("The correct position is specific for your device...") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.4) - (height / 2) } - Text { + GText { width: parent.width * 0.5 - text: "%2".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")).arg(qsTr("To mobile devices")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: "%2".arg(qsTr("https://www.ausweisapp.bund.de/mobile-geraete/")).arg(qsTr("To mobile devices")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary font.bold: true font.underline: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap onLinkActivated: Qt.openUrlExternally(link) x: (parent.width * 0.75) - (width / 2) @@ -675,13 +686,12 @@ SectionPage { width: parent.width height: pin6Image.height + Constants.component_spacing * 2 - Text { + GText { width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.05) - (height / 2) @@ -697,28 +707,26 @@ SectionPage { centerX: 0.5 } - Text { + GText { width: parent.width - text: qsTr("6 digits long PIN") + settingsModel.translationTrigger - font.family: "Noto Serif" + //: LABEL ANDROID IOS + text: qsTr("6 digits long PIN") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true - font.pixelSize: Constants.tutorial_content_header_h1_font_size horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 2 x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.2) - (height / 2) } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.9) - (height / 2) @@ -738,15 +746,14 @@ SectionPage { centerX: 0.2 } - Text { + GText { id: noticeText2 width: parent.width * 0.6 - text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content font.bold: true horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.65) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -782,10 +789,17 @@ SectionPage { } } + TutorialStatusBar { + id: statusBar + + shaderSource: sectionPageFlickable + } + TutorialReaderMethodFooter { id: footer width: baseItem.width + shaderSource: sectionPageFlickable onMenuClicked: firePop() onQuitTutorialClicked: { firePop() diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialSeperator.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialSeperator.qml index 7a7c9ca..8cd4e1b 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialSeperator.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialSeperator.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialSpacer.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialSpacer.qml index bf5fb01..c07972c 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialSpacer.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialSpacer.qml @@ -1,19 +1,22 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 Rectangle { property alias text: textContent.text height: textContent.height + 2 * Constants.component_spacing - Text { + GText { id: textContent anchors.centerIn: parent color: Constants.white width: parent.width * 0.8 - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialStatusBar.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialStatusBar.qml new file mode 100644 index 0000000..cf491c1 --- /dev/null +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialStatusBar.qml @@ -0,0 +1,34 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtGraphicalEffects 1.0 + +import Governikus.Global 1.0 + + +Item { + property alias shaderSource: effectSource.sourceItem + + anchors.top: parent.top + height: plugin.safeAreaMargins.top + width: parent.width + + ShaderEffectSource { + id: effectSource + anchors.fill: parent + sourceRect: Qt.rect(parent.x, parent.y, parent.width, parent.height) + } + FastBlur { + anchors.fill: effectSource + source: effectSource + radius: 32 + } + ColorOverlay { + anchors.fill: parent + color: footer.color + Behavior on color { ColorAnimation { duration: Constants.animation_duration } } + opacity: 0.7 + } +} diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialView.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialView.qml index 3f321ac..a9d6f76 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialView.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 @@ -5,21 +9,28 @@ import QtQml.Models 2.10 import QtQml 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + SectionPage { id: root property int lastYPosition: 0 property var lastVisibleItem - property int contentWidth: Constants.is_tablet ? root.width * 0.5 : root.width + property int contentWidth: Math.min(Style.dimens.max_text_width, root.width) - leftTitleBarAction: TitleBarAction { - state: topLevelPage ? "" : "back"; + titleBarVisible: false + automaticSafeAreaMarginHandling: false + navigationAction: NavigationAction { + // On iOS we want to go back to the MoreView even when a section is expanded. + state: topLevelPage ? root.state : "back"; onClicked: state == "back" ? leaveView() : root.state = "" } - headerTitleBarAction: TitleBarAction { id: header; text: qsTr("Tutorial") + settingsModel.translationTrigger; font.bold: true } + //: LABEL ANDROID IOS + title: qsTr("Tutorial") + SettingsModel.translationTrigger onVisibleChanged: { if (visible) { @@ -113,7 +124,8 @@ SectionPage { state = "" collapseAllAnimation.start() navBar.lockedAndHidden = false - if (Constants.is_layout_ios){ + SettingsModel.showSetupAssistantOnStart = false + if (navBar.state === "more") { firePop() } else { navBar.state = "identify" @@ -162,14 +174,16 @@ SectionPage { color: Constants.white } - Flickable { + GFlickable { id: flickable - height: parent.height - footer.height + + height: parent.height width: root.width + topMargin: statusBar.height + scrollBarTopPadding: plugin.safeAreaMargins.top + scrollBarBottomPadding: footer.height contentWidth: flickableContent.width contentHeight: flickableContent.height - maximumFlickVelocity: Constants.scrolling_speed - flickableDirection: Flickable.VerticalFlick Item { width: root.width @@ -183,10 +197,11 @@ SectionPage { TutorialHeader { id: whatHeader width: root.width - height: (flickable.height / 13.0 ) * 3.0 + height: ((flickable.height - flickable.topMargin - footer.height) / 13.0 ) * 3.0 headerImageSource: "qrc:///images/tutorial/main_menu_what_caret.svg" categoryAbove: false - titleText: qsTr("What?") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + titleText: qsTr("What?") + SettingsModel.translationTrigger initY: 0 z: 40 @@ -220,9 +235,10 @@ SectionPage { TutorialHeader { id: whereHeader width: root.width - height: (flickable.height / 13.0 ) * 3.0 + height: ((flickable.height - flickable.topMargin - footer.height) / 13.0 ) * 3.0 headerImageSource: "qrc:///images/tutorial/main_menu_where_caret.svg" - titleText: qsTr("Where?") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + titleText: qsTr("Where?") + SettingsModel.translationTrigger initY: whatHeader.height z: 30 @@ -256,9 +272,10 @@ SectionPage { TutorialHeader { id: howHeader width: root.width - height: (flickable.height / 13.0 ) * 3.0 + height: ((flickable.height - flickable.topMargin - footer.height) / 13.0 ) * 3.0 headerImageSource: "qrc:///images/tutorial/main_menu_how_caret.svg" - titleText: qsTr("How?") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + titleText: qsTr("How?") + SettingsModel.translationTrigger initY: whatHeader.height + whereHeader.height z: 20 @@ -297,10 +314,11 @@ SectionPage { TutorialHeader { id: importantHeader width: root.width - height: (flickable.height / 13.0 ) * 4.0 + height: ((flickable.height - flickable.topMargin - footer.height) / 13.0 ) * 4.0 overlapping: false headerImageSource: "qrc:///images/tutorial/main_menu_important_caret.svg" - titleText: qsTr("Important!") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + titleText: qsTr("Important!") + SettingsModel.translationTrigger initY: whatHeader.height + whereHeader.height + howHeader.height z: 10 @@ -329,19 +347,37 @@ SectionPage { id: importantContent width: root.contentWidth anchors.horizontalCenter: parent.horizontalCenter + + onLetsGoClicked: leaveView() + } + + // We could use a bottom margin instead of this rectangle, but that would result in the content suddenly + // disappearing below the TutorialFooter at the end of the collapse animation (because the section's + // content is NOT clipped between its two surrounding TutorialHeaders). + Rectangle { + color: Constants.white + width: root.width + height: footer.height } } } } + TutorialStatusBar { + id: statusBar + + shaderSource: flickable + } + TutorialFooter { id: footer width: root.width anchors.horizontalCenter: parent.horizontalCenter - color: importantContent.visible && flickable.contentY > importantHeader.y - 1? Constants.tutorial_red - : howContent.visible && flickable.contentY > howHeader.y - 1? Constants.tutorial_blue - : whereContent.visible && flickable.contentY > whereHeader.y - 1? Constants.tutorial_green - : Constants.tutorial_orange + color: importantContent.visible && flickable.contentY > importantHeader.y - 1 ? Style.color.tutorial_important + : howContent.visible && flickable.contentY > howHeader.y - 1 ? Style.color.tutorial_how + : whereContent.visible && flickable.contentY > whereHeader.y - 1 ? Style.color.tutorial_where + : Style.color.tutorial_what + shaderSource: flickable anchors.bottom: parent.bottom backToMenuActive: root.state !== "" diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialWhat.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialWhat.qml index 5a14465..c1c6fcd 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialWhat.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialWhat.qml @@ -1,6 +1,13 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + TutorialContent { id: baseItem @@ -9,22 +16,20 @@ TutorialContent { width: parent.width * 0.9 anchors.horizontalCenter: parent.horizontalCenter - Text { + GText { width: parent.width - text: qsTr("What is the online ID function?") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("What is the online ID function?") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.italic: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { width: parent.width - text: qsTr("You can authenticate yourself in the internet") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("You can use it to authenticate yourself in the internet") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } @@ -45,7 +50,7 @@ TutorialContent { width: parent.width * 0.6 height: width radius: width * 0.5 - color: Constants.tutorial_very_light_grey + color: Style.color.tutorial_box_background x: (parent.width * 0.5) - (width / 2) } @@ -81,43 +86,41 @@ TutorialContent { } } - Text { + GText { width: parent.width * 0.9 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("and use it to deal with administrative paperwork and business matters electronically!") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("and also to deal with administrative paperwork and business matters electronically!") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } TutorialSpacer { width: parent.width - text: qsTr("Alright, but is it secure?") + settingsModel.translationTrigger - color: Constants.tutorial_orange + //: LABEL ANDROID IOS + text: qsTr("Alright, but is it secure?") + SettingsModel.translationTrigger + color: Style.color.tutorial_what } Column { width: parent.width * 0.9 anchors.horizontalCenter: parent.horizontalCenter - Text { + GText { width: parent.width - text: qsTr("Of course, because we use a so called") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("Of course, because we use a so called") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } - Text { + GText { width: parent.width - text: qsTr("Mutual authentication") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Mutual authentication") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } @@ -188,14 +191,13 @@ TutorialContent { } } - Text { + GText { width: parent.width * 0.9 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("... it establishes a secure connection between ID document and service provider.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("... it establishes a secure connection between ID document and service provider.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } } @@ -212,13 +214,12 @@ TutorialContent { height: parent.width * 0.6 width: parent.width - Text { + GText { width: parent.width * 0.35 - text: qsTr("On every authentication you get displayed who wants to access which data") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("On every authentication you get displayed who wants to access which data") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.2) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -226,7 +227,8 @@ TutorialContent { TutorialImage { id: screenshot - source: qsTr("qrc:///images/tutorial/screenshot_cert_en.png") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/screenshot_cert_%1_en.png").arg(Constants.layout) + SettingsModel.translationTrigger z: 3 readonly property real rightX: x + width @@ -259,14 +261,13 @@ TutorialContent { } } - Text { + GText { width: parent.width * 0.95 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("and you consent to the request with your personal PIN.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("and you consent to the request with your personal PIN.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Item { @@ -301,37 +302,35 @@ TutorialContent { width: parent.width * 0.6 height: width radius: width * 0.5 - color: Constants.tutorial_very_light_grey + color: Style.color.tutorial_box_background z: 1 x: (parent.width * 0.5) - (width / 2) } - Text { + GText { width: parent.width - text: qsTr("... is the provider authorized for this?") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("... is the provider authorized for this?") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 2 x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.3) - (height / 2) } - Text { - width: parent.width - text: qsTr("The provider needs an authorization of the Federal Office of Administration.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + GText { + width: parent.width - 2 * Constants.component_spacing + //: LABEL ANDROID IOS + text: qsTr("The provider needs an authorization of the Federal Office of Administration.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 2 x: (parent.width * 0.5) - (width / 2) - y: (parent.height * 0.5) - (height / 2) + y: (parent.height * 0.5) } } @@ -339,14 +338,13 @@ TutorialContent { width: parent.width height: parent.width * 0.3 - Text { + GText { width: parent.width - text: qsTr("Certificate") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Certificate") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 1 x: (parent.width * 0.2) - (width / 2) @@ -422,37 +420,36 @@ TutorialContent { } } - Text { + GText { width: parent.width * 0.9 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Everytime both participants authenticate each other...") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Everytime both participants authenticate each other...") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 1 } } TutorialSpacer { width: parent.width - text: qsTr("... and therefore your data is protected and securely transfered.") + settingsModel.translationTrigger - color: Constants.tutorial_orange + //: LABEL ANDROID IOS + text: qsTr("... and therefore your data is protected and securely transfered.") + SettingsModel.translationTrigger + color: Style.color.tutorial_what } Column { width: parent.width spacing: Constants.component_spacing - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: qsTr("You can also watch a video on YouTube on this topic") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("You can also watch a video on YouTube on this topic") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 2 } @@ -466,6 +463,7 @@ TutorialContent { MouseArea { anchors.fill: parent + //: LABEL ANDROID IOS onClicked: Qt.openUrlExternally(qsTr("https://www.youtube.com/watch?v=fzbUZmHaZp4&index=5&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm")) } } diff --git a/resources/qml/Governikus/TutorialView/+mobile/TutorialWhere.qml b/resources/qml/Governikus/TutorialView/+mobile/TutorialWhere.qml index 725da4e..8afc46c 100644 --- a/resources/qml/Governikus/TutorialView/+mobile/TutorialWhere.qml +++ b/resources/qml/Governikus/TutorialView/+mobile/TutorialWhere.qml @@ -1,19 +1,25 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.SettingsModel 1.0 + TutorialContent { id: baseItem - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("Where can I use the online ID function?") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("Where can I use the online ID function?") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.italic: true horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } TutorialSeperator { @@ -32,14 +38,13 @@ TutorialContent { source: "qrc:///images/tutorial/section_seperator_where.svg" } - Text { + GText { width: parent.width * 0.9 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("On every website of a service provider where you see this icon:") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("On every website of a service provider where you see this icon:") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { @@ -52,14 +57,13 @@ TutorialContent { } - Text { + GText { width: parent.width * 0.8 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("By the way, you can find many services directly in the AusweisApp2 provider list.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + //: LABEL ANDROID IOS + text: qsTr("By the way, you can find many services directly in the AusweisApp2 provider list.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } TutorialSeperator { @@ -67,9 +71,21 @@ TutorialContent { } Image { - source: qsTr("qrc:///images/tutorial/generated/where_providerlist_screenshot_en.svg") + settingsModel.translationTrigger + //: LABEL ANDROID + source: qsTr("qrc:///images/tutorial/generated/where_providerlist_screenshot_android_en.svg") + SettingsModel.translationTrigger width: parent.width height: width * (sourceSize.height / sourceSize.width) + visible: Constants.is_layout_android + fillMode: Image.PreserveAspectFit + } + + Image { + anchors.horizontalCenter: parent.horizontalCenter + //: LABEL IOS + source: qsTr("qrc:///images/tutorial/screenshot_providerlist_ios_en.png") + SettingsModel.translationTrigger + width: parent.width * 0.6 + height: width * (sourceSize.height / sourceSize.width) + visible: Constants.is_layout_ios fillMode: Image.PreserveAspectFit } @@ -77,33 +93,32 @@ TutorialContent { source: "qrc:///images/tutorial/section_seperator_where.svg" } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.9 - text: qsTr("The integrated self-disclosure is a special service to view the data saved on your ID card.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("The integrated self-disclosure is a special service to view the data saved on your ID card.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } TutorialSeperator { source: "qrc:///images/tutorial/section_seperator_where.svg" } - Text { + GText { width: parent.width * 0.8 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("You can access it by clicking \"See my personal data\" on the AusweisApp2 start page, followed by \"Identify now\"") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h2_font_size + //: LABEL ANDROID IOS + text: qsTr("You can access the self-disclosure by clicking \"See my personal data\" on the AusweisApp2 start page, followed by \"Proceed to PIN entry\"") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header_secondary horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { anchors.horizontalCenter: parent.horizontalCenter - source: qsTr("qrc:///images/tutorial/generated/where_identify_now_en.svg") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/generated/where_identify_now_en.svg") + SettingsModel.translationTrigger width: parent.width height: width * (sourceSize.height / sourceSize.width) fillMode: Image.PreserveAspectFit @@ -111,23 +126,24 @@ TutorialContent { TutorialSpacer { width: parent.width - text: qsTr("And this is how it works") + settingsModel.translationTrigger - color: Constants.tutorial_green + //: LABEL ANDROID IOS + text: qsTr("And this is how it works") + SettingsModel.translationTrigger + color: Style.color.tutorial_where } - Text { + GText { width: parent.width * 0.9 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("The AusweisApp2 will always display who wants to access which of your data.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: qsTr("The AusweisApp2 will always display who wants to access which of your data.") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Image { anchors.horizontalCenter: parent.horizontalCenter - source: qsTr("qrc:///images/tutorial/generated/where_userdata_example_en.svg") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + source: qsTr("qrc:///images/tutorial/generated/where_userdata_example_en.svg") + SettingsModel.translationTrigger width: parent.width * 0.8 height: width * (sourceSize.height / sourceSize.width) fillMode: Image.PreserveAspectFit @@ -145,14 +161,15 @@ TutorialContent { fillMode: Image.PreserveAspectFit } - Text { + GText { width: parent.width * 0.8 anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("Now lay down your ID card and place your device on the ID card.") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("Now lay down your ID card and hold the top of your iPhone to the ID card.") + : qsTr("Now lay down your ID card and place your device on the ID card.")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap } Item { @@ -168,13 +185,14 @@ TutorialContent { centerX: 0.2 } - Text { - width: parent.width * 0.6 - text: qsTr("Don't move your device during the procedure!") + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_font_size + GText { + width: parent.width * 0.5 + //: LABEL ANDROID IOS + text: (Constants.is_layout_ios ? qsTr("Don't move your iPhone during the procedure!") + : qsTr("Don't move your device during the procedure!")) + + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_content horizontalAlignment: Text.AlignLeft - wrapMode: Text.WordWrap x: (parent.width * 0.75) - (width / 2) y: (parent.height * 0.5) - (height / 2) @@ -189,13 +207,12 @@ TutorialContent { width: parent.width height: pin6Image.height + Constants.component_spacing * 2 - Text { + GText { width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("Enter") : qsTr("Now")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.05) - (height / 2) @@ -211,28 +228,26 @@ TutorialContent { centerX: 0.5 } - Text { + GText { width: parent.width - text: qsTr("6 digits long PIN") + settingsModel.translationTrigger - font.family: "Noto Serif" + //: LABEL ANDROID IOS + text: qsTr("6 digits long PIN") + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header font.bold: true - font.pixelSize: Constants.tutorial_content_header_h1_font_size horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap z: 2 x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.2) - (height / 2) } - Text { + GText { anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.8 - text: (settingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + settingsModel.translationTrigger - font.family: "Noto Serif" - font.pixelSize: Constants.tutorial_content_header_h1_font_size + //: LABEL ANDROID IOS + text: (SettingsModel.language === "en" ? qsTr("now!") : qsTr("enter!")) + SettingsModel.translationTrigger + textStyle: Style.text.tutorial_header horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap x: (parent.width * 0.5) - (width / 2) y: (parent.height * 0.9) - (height / 2) diff --git a/resources/qml/Governikus/TutorialView/qmldir b/resources/qml/Governikus/TutorialView/qmldir index 9e53a1f..7976d7a 100644 --- a/resources/qml/Governikus/TutorialView/qmldir +++ b/resources/qml/Governikus/TutorialView/qmldir @@ -10,6 +10,7 @@ internal TutorialImage TutorialImage.qml internal TutorialFooter TutorialFooter.qml internal TutorialSeperator TutorialSeperator.qml internal TutorialSpacer TutorialSpacer.qml +internal TutorialStatusBar TutorialStatusBar.qml internal TutorialReaderMethodNfc TutorialReaderMethodNfc.qml internal TutorialReaderMethodSacMobile TutorialReaderMethodSacMobile.qml internal TutorialReaderMethodSacDesktop TutorialReaderMethodSacDesktop.qml @@ -17,4 +18,9 @@ internal TutorialReaderMethodBluetooth TutorialReaderMethodBluetooth.qml internal TutorialReaderMethodFooter TutorialReaderMethodFooter.qml internal TutorialExpandAnimation TutorialExpandAnimation.qml internal TutorialCollapseAnimation TutorialCollapseAnimation.qml +internal SetupAssistantBinaryDecisionView SetupAssistantBinaryDecisionView.qml +internal SetupAssistantCardReaderView SetupAssistantCardReaderView.qml +internal SetupAssistantButton SetupAssistantButton.qml + +SetupAssistantView 1.0 SetupAssistantView.qml TutorialView 1.0 TutorialView.qml diff --git a/resources/qml/Governikus/View/+desktop/Controller.qml b/resources/qml/Governikus/View/+desktop/Controller.qml new file mode 100644 index 0000000..03e77b7 --- /dev/null +++ b/resources/qml/Governikus/View/+desktop/Controller.qml @@ -0,0 +1,9 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +Item { + signal nextView(int pName) +} diff --git a/resources/qml/Governikus/View/+desktop/FocusFrame.qml b/resources/qml/Governikus/View/+desktop/FocusFrame.qml index f22e01d..6f17b3b 100644 --- a/resources/qml/Governikus/View/+desktop/FocusFrame.qml +++ b/resources/qml/Governikus/View/+desktop/FocusFrame.qml @@ -1,21 +1,31 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 Rectangle { + id: baseItem + property Item framee: parent property Item scope: parent property real marginFactor: 1 + property bool dynamic: true - readonly property real size: Math.max(Math.min(framee.width, framee.height) / 32, 1) + readonly property real staticSize: Math.max(ApplicationModel.scaleFactor * 4, 1) + readonly property real dynamicSize: Math.max(Math.min(framee.width, framee.height) / 32, 1) + readonly property real size: dynamic ? dynamicSize : staticSize - id: border anchors.fill: framee anchors.margins: marginFactor * -size * 2 radius: size * 2 - border.width: scope.focus ? size : 0; + border.width: scope.activeFocus ? size : 0; border.color: Constants.white opacity: 0.5 - color: "transparent" + color: Style.color.transparent } diff --git a/resources/qml/Governikus/View/+desktop/FocusPoint.qml b/resources/qml/Governikus/View/+desktop/FocusPoint.qml new file mode 100644 index 0000000..059440b --- /dev/null +++ b/resources/qml/Governikus/View/+desktop/FocusPoint.qml @@ -0,0 +1,29 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.Type.ApplicationModel 1.0 + + +Text { + id: border + + property Item scope: parent + + width: height + horizontalAlignment: Text.AlignHCenter + + anchors.top: parent.top + anchors.left: parent.left + + visible: scope.activeFocus + + text: "✱" + opacity: 0.5 + color: Style.text.hint.textColor + font.pixelSize: Style.text.hint.textSize +} diff --git a/resources/qml/Governikus/View/+desktop/FramedImage.qml b/resources/qml/Governikus/View/+desktop/FramedImage.qml new file mode 100644 index 0000000..f45f4e9 --- /dev/null +++ b/resources/qml/Governikus/View/+desktop/FramedImage.qml @@ -0,0 +1,37 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 + + +Item { + property alias border: frame.border + property alias source: image.source + property bool circularFrame: true + + Rectangle { + id: frame + + color: Style.color.transparent + border.width: height/20 + border.color: Constants.white + anchors.fill: parent + anchors.centerIn: parent + radius: circularFrame ? width/2 : 0 + } + + Image { + id: image + + fillMode: Image.PreserveAspectFit + anchors.margins: frame.border.width * 2 + anchors.centerIn: frame + anchors.fill: frame + sourceSize.height: frame.height + sourceSize.width: frame.width + } +} diff --git a/resources/qml/Governikus/View/+desktop/SectionPage.qml b/resources/qml/Governikus/View/+desktop/SectionPage.qml index 1ce8016..c18ee0c 100644 --- a/resources/qml/Governikus/View/+desktop/SectionPage.qml +++ b/resources/qml/Governikus/View/+desktop/SectionPage.qml @@ -1,20 +1,44 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 +import QtQuick.Controls 2.3 import Governikus.TitleBar 1.0 +import Governikus.View 1.0 -Item { +Controller { + id: controller + + // Keep in sync with the order/length main.qml contentLoader sectionPages enum Views { - Main = 1, + Main = 0, + SelfAuthentication, Identify, - Provider + ChangePin, + Provider, + Information, + Settings, + History, + SetupAssistant + } + + property bool isAbstract: false + property TitleBarAction titleBarAction: null + readonly property bool sectionPageTypeMarker: true + function setActive() { + if (visible && !isAbstract) { + forceActiveFocus() + } } anchors.fill: parent - signal nextView(int pName) + Accessible.role: Accessible.Grouping + activeFocusOnTab: !isAbstract - readonly property bool sectionPageTypeMarker: true + onVisibleChanged: setActive() - property TitleBarAction titleBarAction: null - property Item navSuccessor: null + FocusPoint {} } diff --git a/resources/qml/Governikus/View/+mobile/+android/ContentArea.qml b/resources/qml/Governikus/View/+mobile/+android/ContentArea.qml index 14ef4ce..db67567 100644 --- a/resources/qml/Governikus/View/+mobile/+android/ContentArea.qml +++ b/resources/qml/Governikus/View/+mobile/+android/ContentArea.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 @@ -11,10 +15,12 @@ import Governikus.IdentifyView 1.0 import Governikus.ProviderView 1.0 import Governikus.HistoryView 1.0 import Governikus.View 1.0 +import Governikus.Type.SettingsModel 1.0 + Item { id: baseItem - property bool ready: settingsModel.showTutorialOnStart ? tutorialView.ready : identifyView.ready + property bool ready: SettingsModel.showSetupAssistantOnStart ? tutorialView.ready : identifyView.ready readonly property var visibleItem: visibleChildren[0] readonly property var currentSectionPage: if (visibleItem) visibleItem.currentSectionPage @@ -52,7 +58,10 @@ Item { anchors.fill: parent visible: baseItem.state === "remoteservice" prefetch: baseItem.ready - sourceComponent: RemoteServiceView {} + sourceComponent: RemoteServiceView { + width: baseItem.width + height: baseItem.height + } } TabBarView { @@ -74,7 +83,7 @@ Item { anchors.fill: parent visible: baseItem.state === "information" prefetch: baseItem.ready - sourceComponent: Information {} + sourceComponent: InformationView {} } TabBarView { diff --git a/resources/qml/Governikus/View/+mobile/+ios/ContentArea.qml b/resources/qml/Governikus/View/+mobile/+ios/ContentArea.qml index 7c06c0f..a977167 100644 --- a/resources/qml/Governikus/View/+mobile/+ios/ContentArea.qml +++ b/resources/qml/Governikus/View/+mobile/+ios/ContentArea.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 @@ -5,12 +9,14 @@ import Governikus.ChangePinView 1.0 import Governikus.MoreView 1.0 import Governikus.IdentifyView 1.0 import Governikus.ProviderView 1.0 -import Governikus.HistoryView 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.TutorialView 1.0 +import Governikus.RemoteServiceView 1.0 import Governikus.View 1.0 Item { id: baseItem - property alias ready: identifyView.ready + property bool ready: SettingsModel.showSetupAssistantOnStart ? tutorialView.ready : identifyView.ready readonly property var visibleItem: visibleChildren[0] readonly property var currentSectionPage: if (visibleItem) visibleItem.currentSectionPage @@ -18,35 +24,46 @@ Item { id: identifyView anchors.fill: parent visible: baseItem.state === "identify" + prefetch: baseItem.ready sourceComponent: IdentifyView {} } TabBarView { anchors.fill: parent visible: baseItem.state === "provider" - prefetch: identifyView.ready + prefetch: baseItem.ready sourceComponent: ProviderView {} } TabBarView { anchors.fill: parent - visible: baseItem.state === "history" - prefetch: identifyView.ready - sourceComponent: HistoryView {} + visible: baseItem.state === "remoteservice" + prefetch: baseItem.ready + sourceComponent: RemoteServiceView { + width: baseItem.width + height: baseItem.height + } } TabBarView { id: pinView anchors.fill: parent visible: baseItem.state === "pin" - prefetch: identifyView.ready + prefetch: baseItem.ready sourceComponent: ChangePinView {} } TabBarView { anchors.fill: parent visible: baseItem.state === "more" - prefetch: identifyView.ready + prefetch: baseItem.ready sourceComponent: MoreView {} } + + TabBarView { + id: tutorialView + visible: baseItem.state === "tutorial" + anchors.fill: parent + sourceComponent: TutorialView {} + } } diff --git a/resources/qml/Governikus/View/+mobile/ContentAreaLoader.qml b/resources/qml/Governikus/View/+mobile/ContentAreaLoader.qml index 0918ff8..2b69bb7 100644 --- a/resources/qml/Governikus/View/+mobile/ContentAreaLoader.qml +++ b/resources/qml/Governikus/View/+mobile/ContentAreaLoader.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 @@ -8,6 +12,11 @@ import Governikus.Global 1.0 // parses the given source. Item { id: baseItem + + function reselectedState() { + visibleItem.popAll() + } + property bool ready: false readonly property var visibleItem: if (loader.item) loader.item.visibleChildren[0] readonly property var currentSectionPage: if (visibleItem) visibleItem.currentSectionPage diff --git a/resources/qml/Governikus/View/+mobile/FocusFrame.qml b/resources/qml/Governikus/View/+mobile/FocusFrame.qml new file mode 100644 index 0000000..3206ad7 --- /dev/null +++ b/resources/qml/Governikus/View/+mobile/FocusFrame.qml @@ -0,0 +1,12 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 + +Item { + property Item framee + property Item scope + property real marginFactor + property bool dynamic +} diff --git a/resources/qml/Governikus/View/+mobile/SectionPage.qml b/resources/qml/Governikus/View/+mobile/SectionPage.qml index 61e0f57..1c2b0fa 100644 --- a/resources/qml/Governikus/View/+mobile/SectionPage.qml +++ b/resources/qml/Governikus/View/+mobile/SectionPage.qml @@ -1,50 +1,72 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 Item { signal firePush(var pSectionPage) + signal firePushWithProperties(var pSectionPage, var pProperties) signal fireReplace(var pSectionPage) signal firePop() signal firePopAll() + function scrollPageDown() { + sectionPageFlickable.scrollPageDown() + } + + function scrollPageUp() { + sectionPageFlickable.scrollPageUp() + } + + function highlightScrollbar() { + sectionPageFlickable.highlightScrollbar() + } + + property var onActivated: highlightScrollbar + property bool pushed: false property bool topLevelPage: false - property var leftTitleBarAction: TitleBarAction {} - property var headerTitleBarAction: TitleBarAction {} - property var rightTitleBarAction: Item {} - property var subTitleBarAction: Item {} + property var navigationAction: null + property string title: null + property var rightTitleBarAction: null + property var subTitleBarAction: null - property color titleBarColor: Constants.blue + property bool titleBarVisible: true + property color titleBarColor: Style.color.accent /* if the header component has a property named titleBarOpacity, use it, otherwise use default value*/ readonly property real titleBarOpacity: header !== null && typeof(header.titleBarOpacity) != "undefined" ? header.titleBarOpacity : 1 + // If a header is set, it is shown as background of the TitleBar, so we need to expand the height + property bool contentBehindTitlebar: header !== null && typeof(header.titleBarOpacity) != "undefined" - property QtObject header: null - property QtObject content: null - property alias contentY: flickable.contentY - property bool disableFlicking: false + // Main flickable of this view + property var sectionPageFlickable: flickable - Flickable { - property real startContentY: 0 + // When enabled the section page will automatically add a safeAreaMargin to the bottom of the page + property bool automaticSafeAreaMarginHandling: true + + // Default header/content of flickable + property QtObject header + property QtObject content + + GFlickable { id: flickable - clip: true - flickableDirection: Flickable.VerticalFlick + + height: contentBehindTitlebar ? (parent.height + Style.dimens.titlebar_height) : parent.height + width: parent.width + anchors.bottom: parent.bottom + + scrollBarTopPadding: contentBehindTitlebar ? Style.dimens.titlebar_height : 0 + contentWidth: flickableContent.width contentHeight: flickableContent.height - anchors.bottom: parent.bottom - width: parent.width - maximumFlickVelocity: Constants.scrolling_speed - /* if a header is set, it is shown as background of the TitleBar, so we need to expand the height*/ - height: header !== null ? parent.height + Constants.titlebar_height : parent.height - onMovementStarted: { - startContentY = contentY - } - onContentYChanged: { - if (disableFlicking || contentY < 0) { contentY = 0 /* prevent flicking over the top */} - } + Column { id: flickableContent diff --git a/resources/qml/Governikus/View/+mobile/TabBarView.qml b/resources/qml/Governikus/View/+mobile/TabBarView.qml index 8e24e9b..d15bbb2 100644 --- a/resources/qml/Governikus/View/+mobile/TabBarView.qml +++ b/resources/qml/Governikus/View/+mobile/TabBarView.qml @@ -1,8 +1,13 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import Governikus.Global 1.0 +import Governikus.Style 1.0 Item { id: baseItem @@ -25,22 +30,46 @@ Item { property var pendingSignals: [] - function push(pSectionPage) { + function disconnectSectionPageSignals(pSectionPage) { + if (pSectionPage === null) { + console.warn("tried to disconnect signals from null") + return + } + + pSectionPage.pushed = false + pSectionPage.firePush.disconnect(baseItem.push) + pSectionPage.firePushWithProperties.disconnect(baseItem.push) + pSectionPage.fireReplace.disconnect(baseItem.replace) + pSectionPage.firePop.disconnect(baseItem.pop) + pSectionPage.firePopAll.disconnect(baseItem.popAll) + } + + function connectSectionPageSignals(pSectionPage) { + if (pSectionPage === null) { + console.warn("tried to connect signals to null") + return + } + + pSectionPage.firePush.connect(baseItem.push) + pSectionPage.firePushWithProperties.connect(baseItem.push) + pSectionPage.fireReplace.connect(baseItem.replace) + pSectionPage.firePop.connect(baseItem.pop) + pSectionPage.firePopAll.connect(baseItem.popAll) + pSectionPage.pushed = true + } + + function push(pSectionPage, pProperties) { if (stack.currentItem === pSectionPage) { return } if (baseItem.pushed) { - pSectionPage.firePush.connect(baseItem.push) - pSectionPage.fireReplace.connect(baseItem.replace) - pSectionPage.firePop.connect(baseItem.pop) - pSectionPage.firePopAll.connect(baseItem.popAll) - stack.push(pSectionPage) - pSectionPage.pushed = true + let item = stack.push(pSectionPage, pProperties) + connectSectionPageSignals(item) } // Main item has not been loaded yet, delay push. else { - baseItem.pendingItems.push({ "item": pSectionPage }) + baseItem.pendingItems.push({ "item": pSectionPage, "properties" : pProperties }) } } @@ -58,12 +87,14 @@ Item { pSectionPage.pushed = false var item = stack.currentItem item.firePush.disconnect(baseItem.push) + item.firePushWithProperties.disconnect(baseItem.push) item.fireReplace.disconnect(baseItem.replace) item.firePop.disconnect(baseItem.pop) item.firePopAll.disconnect(baseItem.popAll) var page = stack.replace(pSectionPage) page.firePush.connect(baseItem.push) + page.firePushWithProperties.connect(baseItem.push) page.fireReplace.connect(baseItem.replace) page.firePop.connect(baseItem.pop) page.firePopAll.connect(baseItem.popAll) @@ -72,18 +103,15 @@ Item { function pop() { var sectionPage = stack.pop() - sectionPage.pushed = false - sectionPage.firePush.disconnect(baseItem.push) - sectionPage.fireReplace.disconnect(baseItem.replace) - sectionPage.firePop.disconnect(baseItem.pop) - sectionPage.firePopAll.disconnect(baseItem.popAll) + disconnectSectionPageSignals(sectionPage) } - function popAll() { - while (stack.depth > 1) { - d.pop() + for (let i = stack.depth - 1; i > 0; i--) { + disconnectSectionPageSignals(stack.get(i)) } + + stack.pop(null) } // Workaround for QTBUG-57267 @@ -126,6 +154,46 @@ Item { anchors.fill: parent } + MouseArea { + id: iosBackGestureMouseArea + + readonly property real minSwipeDistance: parent.width * 0.2 + readonly property real minVelocity: 10 + readonly property real touchStartAreaWidth: 10 + property real startPosX: 0.0 + property real previousPosX: 0.0 + property real velocity: 0.0 + + anchors.fill: parent + + enabled: Constants.is_layout_ios + preventStealing: true + + onPressed: { + if (mouse.x < touchStartAreaWidth && currentSectionPage.navigationAction.state === "back") { + mouse.accepted = true + startPosX = mouse.x + previousPosX = startPosX + velocity = 0.0 + } else { + mouse.accepted = false + } + } + + onPositionChanged: { + let currentVelocity = mouse.x - previousPosX + velocity = (velocity + currentVelocity) / 2.0 + previousPosX = mouse.x + } + + onReleased: { + let swipeDistance = mouse.x - startPosX + if (swipeDistance > minSwipeDistance && velocity > minVelocity) { + currentSectionPage.navigationAction.clicked() + } + } + } + Timer { id: pendingSignalsTimer interval: 100 @@ -133,8 +201,8 @@ Item { onTriggered: d.handlePendingSignals() } - function push(pSectionPage) { - d.pendingSignals.push(function() {d.push(pSectionPage)}) + function push(pSectionPage, pProperties) { + d.pendingSignals.push(function() {d.push(pSectionPage, pProperties)}) d.handlePendingSignals() } @@ -155,4 +223,16 @@ Item { d.pendingSignals.push(function() {d.popAll()}) d.handlePendingSignals() } + + onVisibleChanged: { + if (currentSectionPage) { + currentSectionPage.onActivated() + } + } + + onCurrentSectionPageChanged: { + if (currentSectionPage) { + currentSectionPage.onActivated() + } + } } diff --git a/resources/qml/Governikus/View/qmldir b/resources/qml/Governikus/View/qmldir index f119d43..87bf17f 100644 --- a/resources/qml/Governikus/View/qmldir +++ b/resources/qml/Governikus/View/qmldir @@ -2,6 +2,9 @@ module View ContentArea 1.0 ContentArea.qml ContentAreaLoader 1.0 ContentAreaLoader.qml +Controller 1.0 Controller.qml SectionPage 1.0 SectionPage.qml TabBarView 1.0 TabBarView.qml FocusFrame 1.0 FocusFrame.qml +FocusPoint 1.0 FocusPoint.qml +FramedImage 1.0 FramedImage.qml diff --git a/resources/qml/Governikus/WhiteListClient/+mobile/WhiteListSurveyView.qml b/resources/qml/Governikus/WhiteListClient/+mobile/WhiteListSurveyView.qml index c5e8920..429b87d 100644 --- a/resources/qml/Governikus/WhiteListClient/+mobile/WhiteListSurveyView.qml +++ b/resources/qml/Governikus/WhiteListClient/+mobile/WhiteListSurveyView.qml @@ -1,152 +1,240 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.TitleBar 1.0 import Governikus.View 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.AuthModel 1.0 +import Governikus.Type.SurveyModel 1.0 + SectionPage { id: root - leftTitleBarAction: TitleBarAction { state: "cancel"; onClicked: root.done(false) } - headerTitleBarAction: TitleBarAction { text: qsTr("Feedback") + settingsModel.translationTrigger; font.bold: true } signal done(bool pUserAccepted) + navigationAction: NavigationAction { state: "cancel"; onClicked: root.done(false) } + //: LABEL ANDROID IOS + title: qsTr("Feedback") + SettingsModel.translationTrigger + + QtObject { + id: d + + property bool dataHidden: true + } + content: Column { width: root.width + padding: Constants.pane_padding spacing: Constants.pane_spacing Pane { id: whitePane + anchors.margins: Constants.pane_padding - title: qsTr("Send device data?") + settingsModel.translationTrigger + //: INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Header + title: qsTr("Send device data?") + SettingsModel.translationTrigger - Text { + GText { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Would you like to help us to improve the AusweisApp2?") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - wrapMode: Text.WordWrap - } - Text { - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("Supplying your device characteristics helps us to gather reliable information about the compatibility of your device.") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - wrapMode: Text.WordWrap - } - Text { - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("The transmission is anonymous. No personal data is collected or transmitted!") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - wrapMode: Text.WordWrap - } - Text { - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("The following information will be transmitted, if you decide so:") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - wrapMode: Text.WordWrap + //: INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Part of content text + text: qsTr("Would you like to help us to improve the AusweisApp2?") + SettingsModel.translationTrigger + textStyle: Style.text.normal } - Item { + GText { anchors.left: parent.left anchors.right: parent.right - height: Math.max(column1.height, column2.height) + //: INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Part of content text + text: qsTr("Supplying your device characteristics helps us to gather reliable information about the compatibility of your device.") + SettingsModel.translationTrigger + textStyle: Style.text.normal + } - ListModel { - id: leftInformationModel - ListElement { - entry: QT_TR_NOOP("Vendor") - } - ListElement { - entry: QT_TR_NOOP("Model number") - } - ListElement { - entry: QT_TR_NOOP("Model name") - } - ListElement { - entry: QT_TR_NOOP("Collection date") - } - ListElement { - entry: QT_TR_NOOP("AusweisApp2 version") - } - } + GText { + anchors.left: parent.left + anchors.right: parent.right + //: INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Part of content text + text: qsTr("The transmission is anonymous. No personal data is collected or transmitted!") + SettingsModel.translationTrigger + textStyle: Style.text.normal + } - ListModel { - id: rightInformationModel - ListElement { - entry: QT_TR_NOOP("ROM build number") - } - ListElement { - entry: QT_TR_NOOP("Android version") - } - ListElement { - entry: QT_TR_NOOP("Kernel version") - } - ListElement { - entry: QT_TR_NOOP("Max. NFC packet length") - } - } + Column { + anchors.left: parent.left + anchors.right: parent.right - Column { - id: column1 - width: parent.width / 2 + spacing: 2 + + GSeparator { anchors.left: parent.left - - Repeater { - model: leftInformationModel - delegate: BulletPointDelegate { - text: qsTr(entry) + settingsModel.translationTrigger - } - } + anchors.right: parent.right } - Column { - id: column2 - width: parent.width / 2 + Button { + id: collapsableCollectedData + + height: showDataButton.height + Constants.pane_spacing + anchors.left: parent.left anchors.right: parent.right - Repeater { - model: rightInformationModel - delegate: BulletPointDelegate { - text: qsTr(entry) + settingsModel.translationTrigger + states: [ + State { + name: "open"; + when: !d.dataHidden + + PropertyChanges { + target: collapsableCollectedData; + height: collectedData.openHeight + showDataButton.height + Constants.pane_spacing + } + PropertyChanges { + target: collectedData; + height: collectedData.openHeight + } + PropertyChanges { + target: collectedData; + opacity: 1.0 + } + } + ] + transitions: [ + Transition { + PropertyAnimation { + target: collectedData + property: "height" + easing.type: Easing.InOutQuad + duration: 500 + } + PropertyAnimation { + target: collectedData + property: "opacity" + easing.type: Easing.InOutQuad + duration: 500 + } + PropertyAnimation { + target: collapsableCollectedData + property: "height" + easing.type: Easing.InOutQuad + duration: 500 + } + } + ] + + background: Rectangle { + color: collapsableCollectedData.down ? Style.color.tutorial_box_background : Style.color.background_pane + } + + onClicked: { + d.dataHidden = !d.dataHidden + } + + contentItem: Item { + Item { + id: showDataButton + + height: showDataText.height + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + GText { + id: showDataTriangle + + anchors.top: parent.top + anchors.right: parent.right + + rightPadding: Constants.groupbox_spacing + text: d.dataHidden ? "\u25BC" : "\u25B2" + textStyle: Style.text.normal + horizontalAlignment: Text.AlignRight + } + + GText { + id: showDataText + + anchors.right: showDataTriangle.left + anchors.bottom: showDataTriangle.bottom + anchors.bottomMargin: showDataText.height / 8 + + rightPadding: Constants.groupbox_spacing + text: qsTr("Collected data") + SettingsModel.translationTrigger + textStyle: Style.text.normal + } + } + + Item { + id: collectedData + + property real openHeight: dataColumn.implicitHeight + + height: openHeight + width: parent.width + anchors.top: showDataButton.bottom + + opacity: 0 + clip: true + + Column { + id: dataColumn + + width: parent.width + anchors.left: parent.left + + topPadding: Constants.groupbox_spacing + + Repeater { + id: repeater + + model: SurveyModel + delegate: LabeledText { + width: dataColumn.width + label: title + text: value + } + } + } } } } + + GSeparator { + anchors.left: parent.left + anchors.right: parent.right + } } - Text { + GText { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Thank you for your assistance!") + settingsModel.translationTrigger - font.pixelSize: Constants.normal_font_size - font.bold: true - color: Constants.secondary_text - wrapMode: Text.WordWrap + //: INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Thank you message + text: qsTr("Thank you for your assistance!") + SettingsModel.translationTrigger + textStyle: Style.text.normal } } Row { - spacing: Constants.component_spacing height: childrenRect.height anchors.horizontalCenter: parent.horizontalCenter + spacing: Constants.component_spacing + GButton { - text: qsTr("Cancel") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Cancel") + SettingsModel.translationTrigger onClicked: root.done(false) } GButton { - text: qsTr("Transmit") + settingsModel.translationTrigger + //: LABEL ANDROID IOS + text: qsTr("Transmit") + SettingsModel.translationTrigger onClicked: root.done(true) } } diff --git a/resources/qml/Governikus/WhiteListClient/BulletPointDelegate.qml b/resources/qml/Governikus/WhiteListClient/BulletPointDelegate.qml deleted file mode 100644 index e673bf9..0000000 --- a/resources/qml/Governikus/WhiteListClient/BulletPointDelegate.qml +++ /dev/null @@ -1,31 +0,0 @@ -import QtQuick 2.10 - -import Governikus.Global 1.0 - -Item { - width: parent.width - height: content.height - property alias text: content.text - - Text { - id: bullet - anchors.top: parent.top - anchors.left: parent.left - text: "\u2022" - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - } - - Text { - id: content - anchors.top: parent.top - anchors.left: bullet.right - anchors.leftMargin: Constants.groupbox_spacing - anchors.right: parent.right - anchors.rightMargin: Constants.groupbox_spacing - - font.pixelSize: Constants.normal_font_size - color: Constants.secondary_text - wrapMode: Text.WordWrap - } -} diff --git a/resources/qml/Governikus/WhiteListClient/qmldir b/resources/qml/Governikus/WhiteListClient/qmldir index 0ece239..d9bcf4f 100644 --- a/resources/qml/Governikus/WhiteListClient/qmldir +++ b/resources/qml/Governikus/WhiteListClient/qmldir @@ -1,5 +1,3 @@ module WhiteListClient -internal BulletPointDelegate BulletPointDelegate.qml - WhiteListSurveyView 1.0 WhiteListSurveyView.qml diff --git a/resources/qml/Governikus/Workflow/+desktop/GeneralWorkflow.qml b/resources/qml/Governikus/Workflow/+desktop/GeneralWorkflow.qml new file mode 100644 index 0000000..8ade5bb --- /dev/null +++ b/resources/qml/Governikus/Workflow/+desktop/GeneralWorkflow.qml @@ -0,0 +1,206 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import Governikus.Global 1.0 +import Governikus.Style 1.0 +import Governikus.View 1.0 +import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 +import Governikus.Type.AuthModel 1.0 +import Governikus.Type.NumberModel 1.0 +import Governikus.Type.CardReturnCode 1.0 +import Governikus.Type.ReaderPlugIn 1.0 + + +SectionPage +{ + property int waitingFor: 0 + property bool isPinChange: false + + Accessible.name: qsTr("General workflow view") + SettingsModel.translationTrigger + Accessible.description: qsTr("This is the general workflow view of the AusweisApp2.") + SettingsModel.translationTrigger + + QtObject { + id: d + + readonly property bool foundSelectedReader: ApplicationModel.foundSelectedReader + readonly property bool foundPCSCReader: ApplicationModel.foundSelectedReader && ApplicationModel.isReaderTypeAvailable(ReaderPlugIn.PCSC) + readonly property bool foundRemoteReader: ApplicationModel.foundSelectedReader && ApplicationModel.isReaderTypeAvailable(ReaderPlugIn.REMOTE) + //: LABEL DESKTOP_QML + readonly property string purpose: (isPinChange ? qsTr("Change PIN") : qsTr("Identify")) + SettingsModel.translationTrigger + } + + + + Connections { + target: ApplicationModel + onFireCertificateRemoved: { + //: INFO DESKTOP_QML The paired devices was removed since it did not respond to connection attempts. It needs to be paired again if it should be used as card reader. + ApplicationModel.showFeedback(qsTr("The device %1 was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself.").arg(pDeviceName)) + } + } + + GText { + visible: retryCounter.visible + anchors.horizontalCenter: retryCounter.horizontalCenter + anchors.bottom: retryCounter.top + anchors.bottomMargin: Constants.component_spacing + + font.bold: true + //: LABEL DESKTOP_QML + text: qsTr("Attempts") + SettingsModel.translationTrigger + textStyle: Style.text.normal + } + + StatusIcon { + id: retryCounter + + visible: NumberModel.retryCounter >= 0 && NumberModel.passwordType === NumberModel.PASSWORD_PIN + height: Style.dimens.status_icon_small + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: height + + activeFocusOnTab: true + Accessible.name: qsTr("Remaining attempts:") + " " + NumberModel.retryCounter + SettingsModel.translationTrigger + + text: NumberModel.retryCounter + + FocusFrame {} + } + + StatusIcon { + height: Style.dimens.status_icon_large + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.top + anchors.verticalCenterOffset: parent.height / 4 + + busy: true + source: AuthModel.readerImage + } + + ProgressCircle { + id: progressCircle + + visible: waitingFor !== Workflow.WaitingFor.None + anchors.top: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + activeFocusOnTab: true + Accessible.role: Accessible.ProgressBar + Accessible.name: qsTr("Step %1 of 3").arg(state) + SettingsModel.translationTrigger + + state: switch (waitingFor) { + case Workflow.WaitingFor.Reader: + return d.foundSelectedReader ? "2" : "1" + case Workflow.WaitingFor.Card: + return "2" + case Workflow.WaitingFor.Password: + return "3" + default: + return "1" + } + + FocusFrame {} + } + + GText { + id: mainText + + visible: text !== "" + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: progressCircle.bottom + anchors.topMargin: Constants.component_spacing + + activeFocusOnTab: true + Accessible.role: Accessible.Paragraph + Accessible.name: mainText.text + + text: { + SettingsModel.translationTrigger + + switch (waitingFor) { + case Workflow.WaitingFor.Reader: + if (ApplicationModel.extendedLengthApdusUnsupported) { + //: ERROR DESKTOP_QML + return qsTr("The used card reader does not meet the technical requirements (Extended Length not supported).") + } + //: LABEL DESKTOP_QML + return d.foundSelectedReader ? d.purpose : qsTr("Establish connection") + case Workflow.WaitingFor.Card: + if (NumberModel.pinDeactivated) { + //: LABEL DESKTOP_QML + return qsTr("Information") + SettingsModel.translationTrigger + } + return d.purpose + case Workflow.WaitingFor.Password: + //: LABEL DESKTOP_QML + return qsTr("Information") + SettingsModel.translationTrigger + default: + return "" + } + } + textStyle: Style.text.header + + horizontalAlignment: Text.AlignHCenter + FocusFrame {} + } + + GText { + id: subText + + readonly property string requestCardText: { + if (d.foundPCSCReader && !d.foundRemoteReader) { + //: INFO DESKTOP_QML The AA2 is waiting for an id card to be inserted into the card reader. + return qsTr("Please insert the card into the device") + } else if (!d.foundPCSCReader && d.foundRemoteReader) { + //: INFO DESKTOP_QML The AA2 is waiting for the smartphone to be placed on the id. + return qsTr("Please place the smartphone on the card") + } + + //: INFO DESKTOP_QML The AA2 is waiting for an id card to be inserted into the card reader (or smartphone for that matter). + return qsTr("Please place the smartphone on the card or insert the card into the device") + } + + visible: text !== "" && !ApplicationModel.extendedLengthApdusUnsupported + width: Math.min(parent.width - (2 * Constants.pane_padding), Style.dimens.max_text_width) + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: Constants.text_spacing + + activeFocusOnTab: true + Accessible.role: Accessible.Paragraph + Accessible.name: subText.text + + text: { + SettingsModel.translationTrigger + + switch (waitingFor) { + case Workflow.WaitingFor.Reader: + //: INFO DESKTOP_QML AA2 is waiting for the card reader or the id card. + return d.foundSelectedReader ? requestCardText : qsTr("Searching for card reader") + case Workflow.WaitingFor.Card: + if (NumberModel.pinDeactivated) { + //: INFO DESKTOP_QML The online authentication feature of the card is disabled and needs to be activated by the authorities. + return qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") + } + return requestCardText + case Workflow.WaitingFor.Password: + //: INFO DESKTOP_QML The card reader is a comfort reader with its own display, the user is requested to pay attention to that display (instead of the AA2). + return qsTr("Please observe the display of your card reader.") + default: + return "" + } + } + textStyle: Style.text.header_secondary + + horizontalAlignment: Text.AlignHCenter + + FocusFrame {} + } +} diff --git a/resources/qml/Governikus/Workflow/+desktop/ProgressCircle.qml b/resources/qml/Governikus/Workflow/+desktop/ProgressCircle.qml index 8ab3f2b..0b9f2f0 100644 --- a/resources/qml/Governikus/Workflow/+desktop/ProgressCircle.qml +++ b/resources/qml/Governikus/Workflow/+desktop/ProgressCircle.qml @@ -1,6 +1,11 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 import Governikus.Type.ApplicationModel 1.0 @@ -55,7 +60,7 @@ Item { anchors.verticalCenter: parent.verticalCenter width: d.stepWidth * 2 height: ApplicationModel.scaleFactor * 8 - color: Constants.grey_light + color: Style.color.border } TextCircle { @@ -84,7 +89,7 @@ Item { height: circle1.height + ApplicationModel.scaleFactor * 40 width: height anchors.centerIn: parent - color: "transparent" + color: Style.color.transparent radius: height / 2 border.color: Constants.white border.width: ApplicationModel.scaleFactor * 6 diff --git a/resources/qml/Governikus/Workflow/+desktop/TextCircle.qml b/resources/qml/Governikus/Workflow/+desktop/TextCircle.qml index fc03d10..6bc3a4c 100644 --- a/resources/qml/Governikus/Workflow/+desktop/TextCircle.qml +++ b/resources/qml/Governikus/Workflow/+desktop/TextCircle.qml @@ -1,7 +1,12 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 import Governikus.Global 1.0 +import Governikus.Style 1.0 Rectangle { @@ -11,13 +16,12 @@ Rectangle { width: height enabled: false radius: width * 0.5 - color: enabled ? Constants.blue : Constants.white + color: enabled ? Style.color.accent : Constants.white - Text { + GText { id: number anchors.centerIn: parent font.bold: true - font.pixelSize: Constants.header_font_size - color: parent.enabled ? Constants.white : Constants.blue + textStyle: parent.enabled ? Style.text.header : Style.text.header_accent } } diff --git a/resources/qml/Governikus/Workflow/+mobile/BluetoothWorkflow.qml b/resources/qml/Governikus/Workflow/+mobile/BluetoothWorkflow.qml index 75436e5..c56c98c 100644 --- a/resources/qml/Governikus/Workflow/+mobile/BluetoothWorkflow.qml +++ b/resources/qml/Governikus/Workflow/+mobile/BluetoothWorkflow.qml @@ -1,9 +1,14 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.1 import Governikus.Global 1.0 import Governikus.TechnologyInfo 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ReaderPlugIn 1.0 import Governikus.Type.NumberModel 1.0 @@ -47,14 +52,15 @@ Item { TechnologyInfo { id: techInfo anchors.left: parent.left - anchors.leftMargin: Utils.dp(5) + anchors.leftMargin: 5 anchors.right: parent.right anchors.rightMargin: anchors.leftMargin anchors.top: progressIndicator.bottom anchors.bottom: technologySwitch.top enableButtonVisible: ApplicationModel.bluetoothAvailable && (!ApplicationModel.bluetoothEnabled || parent.showLocationPermissionInfo) - enableButtonText: (!ApplicationModel.bluetoothEnabled ? qsTr("Enable Bluetooth") : qsTr("Continue")) + settingsModel.translationTrigger + //: LABEL ANDROID IOS + enableButtonText: (!ApplicationModel.bluetoothEnabled ? qsTr("Enable Bluetooth") : qsTr("Continue")) + SettingsModel.translationTrigger onEnableClicked: { if (!ApplicationModel.bluetoothEnabled) { ApplicationModel.bluetoothEnabled = true @@ -63,21 +69,30 @@ Item { } } enableText: (!visible ? "" + //: INFO ANDROID IOS Bluetooth is not available to AA2, the user is requested to use NFC instead. : !ApplicationModel.bluetoothAvailable ? qsTr("Bluetooth is not supported by your device.") + "
" + qsTr("Please try NFC.") + //: INFO ANDROID IOS Bluetooth is available but not active. : !ApplicationModel.bluetoothEnabled ? qsTr("Bluetooth is switched off.") + "
" + qsTr("Please enable Bluetooth.") + //: INFO ANDROID IOS Bluetooth is active but the AA2 does not have the location permission which is required to find the bluetooth reader. The user is asked to give the permission to the AA2 after pressing the "ok" button. : parent.showLocationPermissionInfo ? qsTr("No paired and activated Bluetooth device was detected. The AusweisApp2 needs access to your location in order to discover available devices. You can grant this permission after clicking the continue button.") + //: INFO ANDROID IOS The connection to the bluetooth reader failed. : !ApplicationModel.bluetoothResponding ? qsTr("An error occured while connecting to your bluetooth device. Try to pair your device in the system settings and restart the app.") - : "") + settingsModel.translationTrigger + : "") + SettingsModel.translationTrigger + //: INFO ANDROID IOS Status message while connecting to the bluetooth reader - Header titleText: ((baseItem.waitingFor === Workflow.WaitingFor.Reader) ? qsTr("Establish connection") + //: INFO ANDROID IOS Status message while the connection to the bluetooth has been established and an id card needs to be inserted - Header : (baseItem.waitingFor === Workflow.WaitingFor.Card) ? qsTr("Determine card") - : "") + settingsModel.translationTrigger + : "") + SettingsModel.translationTrigger + //: LABEL ANDROID IOS subTitleText: (!visible ? "" : !!NumberModel.inputError ? NumberModel.inputError + //: INFO ANDROID IOS Status message while the connection to the bluetooth reader is being established. : (baseItem.waitingFor === Workflow.WaitingFor.Reader) ? qsTr("Search card reader...") + //: INFO ANDROID IOS Status message after the connection to the bluetooth reader is established and an id card needs to be inserted. : (baseItem.waitingFor === Workflow.WaitingFor.Card) ? qsTr("Please insert your ID card.") - : "") + settingsModel.translationTrigger + : "") + SettingsModel.translationTrigger } TechnologySwitch { diff --git a/resources/qml/Governikus/Workflow/+mobile/BusyImageIndicator.qml b/resources/qml/Governikus/Workflow/+mobile/BusyImageIndicator.qml index a4b49c3..aaeb4e7 100644 --- a/resources/qml/Governikus/Workflow/+mobile/BusyImageIndicator.qml +++ b/resources/qml/Governikus/Workflow/+mobile/BusyImageIndicator.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 diff --git a/resources/qml/Governikus/Workflow/+mobile/CardReader.qml b/resources/qml/Governikus/Workflow/+mobile/CardReader.qml index 4a2d465..c77bd86 100644 --- a/resources/qml/Governikus/Workflow/+mobile/CardReader.qml +++ b/resources/qml/Governikus/Workflow/+mobile/CardReader.qml @@ -1,6 +1,11 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 Item { property bool pinFieldAnimation: true @@ -53,7 +58,7 @@ Item { Rectangle { id: reader - color: "#92cef9" + color: Style.color.card_reader radius: height * 0.05 anchors.bottom: parent.bottom width: parent.width @@ -61,7 +66,7 @@ Item { Rectangle { id: slot - color: "white" + color: Constants.white radius: 10 height: reader.height * 0.05 anchors.top: parent.top @@ -74,7 +79,7 @@ Item { Rectangle { id: card - color: Constants.blue + color: Style.color.id_card radius: height * 0.05 anchors.horizontalCenter: parent.horizontalCenter height: baseItem.height * 1.5 / 7 @@ -84,7 +89,7 @@ Item { Rectangle { id: cardStripe1 - color: "white" + color: Constants.white radius: 10 height: parent.height * 0.1 anchors.top: parent.top @@ -97,7 +102,7 @@ Item { Rectangle { id: cardStripe2 - color: "white" + color: Constants.white radius: cardStripe1.radius height: parent.height * 0.1 anchors.top: cardStripe1.top @@ -134,7 +139,7 @@ Item { Rectangle { readonly property int margin: parent.width * 0.1 id: display - color: "white" + color: Constants.white radius: height * 0.2 height: reader.height * 0.2 anchors.bottom: pinGrid.top @@ -196,7 +201,7 @@ Item { states: [ State { name: "off" - PropertyChanges {target: pinButtonCircle; color: "white"} + PropertyChanges {target: pinButtonCircle; color: Constants.white} PropertyChanges {target: pinButtonCircle; width: pinButton._size} }, State { diff --git a/resources/qml/Governikus/Workflow/+mobile/GeneralWorkflow.qml b/resources/qml/Governikus/Workflow/+mobile/GeneralWorkflow.qml index 5998e93..9dadd55 100644 --- a/resources/qml/Governikus/Workflow/+mobile/GeneralWorkflow.qml +++ b/resources/qml/Governikus/Workflow/+mobile/GeneralWorkflow.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 @@ -21,12 +25,12 @@ SectionPage property int waitingFor: 0 - leftTitleBarAction: TitleBarAction { + navigationAction: NavigationAction { enabled: baseItem.waitingFor !== Workflow.WaitingFor.Password state: enabled ? "cancel" : "hidden" onClicked: workflowModel.cancelWorkflow() } - headerTitleBarAction: TitleBarAction { text: workflowTitle; font.bold: true } + title: workflowTitle NfcWorkflow { diff --git a/resources/qml/Governikus/Workflow/+mobile/NfcProgressIndicator.qml b/resources/qml/Governikus/Workflow/+mobile/NfcProgressIndicator.qml index 6f22a6e..3669ab1 100644 --- a/resources/qml/Governikus/Workflow/+mobile/NfcProgressIndicator.qml +++ b/resources/qml/Governikus/Workflow/+mobile/NfcProgressIndicator.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 @@ -10,7 +14,7 @@ Item { SequentialAnimation { id: shaking loops: Animation.Infinite - readonly property int delta: Utils.dp(4) + readonly property int delta: 4 readonly property int deltaDuration: 300 ParallelAnimation { diff --git a/resources/qml/Governikus/Workflow/+mobile/NfcWorkflow.qml b/resources/qml/Governikus/Workflow/+mobile/NfcWorkflow.qml index ec57ea8..fa75274 100644 --- a/resources/qml/Governikus/Workflow/+mobile/NfcWorkflow.qml +++ b/resources/qml/Governikus/Workflow/+mobile/NfcWorkflow.qml @@ -1,9 +1,14 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.1 import Governikus.Global 1.0 import Governikus.TechnologyInfo 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.ReaderPlugIn 1.0 import Governikus.Type.NumberModel 1.0 @@ -34,28 +39,36 @@ Item { id: technologyInfo anchors.left: parent.left - anchors.leftMargin: Utils.dp(5) + anchors.leftMargin: 5 anchors.right: parent.right anchors.rightMargin: anchors.leftMargin anchors.top: progressIndicatorWrapper.bottom anchors.bottom: technologySwitch.top clip: true - enableButtonVisible: ApplicationModel.nfcAvailable && !ApplicationModel.nfcEnabled - enableButtonText: qsTr("Go to NFC settings") + settingsModel.translationTrigger - onEnableClicked: qmlExtension.showSettings("android.settings.NFC_SETTINGS") + enableButtonVisible: ApplicationModel.nfcAvailable && (!ApplicationModel.nfcEnabled || !ApplicationModel.nfcRunning) + //: LABEL DESKTOP_QML + enableButtonText: (!ApplicationModel.nfcEnabled ? qsTr("Go to NFC settings") : qsTr("Start NFC scan")) + SettingsModel.translationTrigger + onEnableClicked: !ApplicationModel.nfcEnabled ? ApplicationModel.showSettings(ApplicationModel.SETTING_NFC) : ApplicationModel.nfcRunning = true enableText: (!visible ? "" : - !ApplicationModel.nfcAvailable ? qsTr("NFC is not supported by your device.") + "
" + qsTr("Please try Bluetooth.") : - !ApplicationModel.nfcEnabled ? qsTr("NFC is switched off.") + "
" + qsTr("Please enable NFC in your system settings.") : "" - ) + settingsModel.translationTrigger + //: INFO ANDROID IOS AA2 can't use NFC on this device, suggest to use bluetooth instead. + !ApplicationModel.nfcAvailable ? qsTr("NFC is not supported by your device.") + "
" + qsTr("You require an additional 'Bluetooth card reader' or an additional 'smartphone as card reader' to use the online identification function with this device.") : + //: INFO ANDROID IOS NFC is available but needs to be activated in the settings of the smartphone. + !ApplicationModel.nfcEnabled ? qsTr("NFC is switched off.") + "
" + qsTr("Please enable NFC in your system settings.") : + //: INFO ANDROID IOS NFC is available but needs to be activated in the settings of the smartphone. + !ApplicationModel.nfcRunning ? qsTr("NFC scan is not running.") + "
" + qsTr("Please start the NFC scan.") : "" + ) + SettingsModel.translationTrigger - titleText: qsTr("Establish connection") + settingsModel.translationTrigger + titleText: qsTr("Establish connection") + SettingsModel.translationTrigger subTitleText: (!visible ? "" : + //: INFO ANDROID IOS The NFC interface does not meet the minimum requirements, using a bluetooth reader or a differnt smarthpone is suggested. ApplicationModel.extendedLengthApdusUnsupported ? qsTr("Your device does not meet the technical requirements (Extended Length not supported). You require an additional 'Bluetooth card reader' or an additional 'smartphone as card reader' to use the online identification function with this device.") : + //: INFO ANDROID IOS The online authentication feature is disabled and needs to be activated by the authorities. NumberModel.pinDeactivated ? qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function.") : + //: INFO ANDROID IOS The id card may be inserted, the authentication process may be started. qsTr("Please place your device
on your ID card.") - ) + settingsModel.translationTrigger + ) + SettingsModel.translationTrigger } diff --git a/resources/qml/Governikus/Workflow/+mobile/ProgressCircle.qml b/resources/qml/Governikus/Workflow/+mobile/ProgressCircle.qml index 030095f..15071cd 100644 --- a/resources/qml/Governikus/Workflow/+mobile/ProgressCircle.qml +++ b/resources/qml/Governikus/Workflow/+mobile/ProgressCircle.qml @@ -1,18 +1,23 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import Governikus.Global 1.0 +import Governikus.Style 1.0 Item { id: baseItem - height: Utils.dp(70) - readonly property int _stepWidth: Utils.dp(60) + height: 70 + readonly property int _stepWidth: 60 Rectangle { id: rec1 anchors.left: baseItem.horizontalCenter anchors.verticalCenter: parent.verticalCenter width: _stepWidth - height: Utils.dp(2) + height: 2 color: Constants.blue } @@ -20,20 +25,20 @@ Item { id: tCircle1 anchors.verticalCenter: rec1.verticalCenter anchors.horizontalCenter: rec1.left - width: state === "active" ? Utils.dp(70) : Utils.dp(25) + border.width + width: state === "active" ? 70 : 25 + border.width height: width radius: width / 2 - color: Constants.background_color - border.color: "white" + color: Style.color.background + border.color: Constants.white border.width: state === "active" ? 2 : 0 state: "inactive" states: [ State { name: "active" - PropertyChanges { target: innerDisc; width: Utils.dp(50) } + PropertyChanges { target: innerDisc; width: 50 } }, State { name: "inactive" - PropertyChanges { target: innerDisc; width: Utils.dp(25) } + PropertyChanges { target: innerDisc; width: 25 } } ] @@ -46,7 +51,7 @@ Item { Rectangle { id: innerDisc anchors.centerIn: parent - color: tCircle1.state === "active" ? Constants.blue : "white" + color: tCircle1.state === "active" ? Style.color.accent : Constants.white radius: width / 2 height: width border.color: Constants.blue @@ -56,7 +61,7 @@ Item { text: "1" font.bold: true font.pixelSize: parent.height / 3 - color: tCircle1.state === "active" ? "white" : Constants.blue + color: tCircle1.state === "active" ? Constants.white : Style.color.accent } } } diff --git a/resources/qml/Governikus/Workflow/+mobile/ProgressIndicator.qml b/resources/qml/Governikus/Workflow/+mobile/ProgressIndicator.qml index 7ace838..e80dbd1 100644 --- a/resources/qml/Governikus/Workflow/+mobile/ProgressIndicator.qml +++ b/resources/qml/Governikus/Workflow/+mobile/ProgressIndicator.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtGraphicalEffects 1.0 @@ -40,7 +44,7 @@ Item { id: busyIcon anchors.centerIn: parent width: height - height: Math.min(parent.height - Utils.dp(40), 2 * pCircle.height) + height: Math.min(parent.height - 40, 2 * pCircle.height) running: visible visible: baseItem.state === "one" } diff --git a/resources/qml/Governikus/Workflow/+mobile/RemoteWorkflow.qml b/resources/qml/Governikus/Workflow/+mobile/RemoteWorkflow.qml index f4ffaaf..53d2409 100644 --- a/resources/qml/Governikus/Workflow/+mobile/RemoteWorkflow.qml +++ b/resources/qml/Governikus/Workflow/+mobile/RemoteWorkflow.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Layouts 1.1 @@ -5,6 +9,7 @@ import Governikus.Global 1.0 import Governikus.RemoteServiceView 1.0 import Governikus.TechnologyInfo 1.0 import Governikus.Type.ApplicationModel 1.0 +import Governikus.Type.SettingsModel 1.0 import Governikus.Type.RemoteServiceModel 1.0 import Governikus.Type.ReaderPlugIn 1.0 import Governikus.Type.NumberModel 1.0 @@ -21,7 +26,8 @@ Item { Connections { target: ApplicationModel onFireCertificateRemoved: { - qmlExtension.showFeedback(qsTr("The device %1 was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself.").arg(pDeviceName)) + //: INFO ANDROID IOS The paired smartphone was removed since it did not respond to connection attempts. It needs to be paired again before using it. + ApplicationModel.showFeedback(qsTr("The device %1 was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself.").arg(pDeviceName)) } } @@ -45,7 +51,7 @@ Item { TechnologyInfo { id: techInfo anchors.left: parent.left - anchors.leftMargin: Utils.dp(5) + anchors.leftMargin: 5 anchors.right: parent.right anchors.rightMargin: anchors.leftMargin anchors.top: progressIndicator.bottom @@ -53,13 +59,16 @@ Item { enableButtonVisible: !wifiEnabled || !foundSelectedReader enableButtonText: { - settingsModel.translationTrigger + SettingsModel.translationTrigger if (!wifiEnabled) { + //: LABEL DESKTOP_QML return qsTr("Enable Wifi"); } else if (!foundSelectedReader) { + //: LABEL DESKTOP_QML return qsTr("Pair device"); } else { + //: LABEL DESKTOP_QML return qsTr("Continue") } } @@ -72,34 +81,41 @@ Item { } } enableText: { - settingsModel.translationTrigger + SettingsModel.translationTrigger if (!wifiEnabled) { + //: INFO ANDROID IOS The wifi module needs to be enabled in the system settings to use the remote service. return qsTr("To use the remote service WiFi has to be activated. Please activate WiFi in your device settings."); } else if (!foundSelectedReader) { - return qsTr("No paired and activated remote device was detected. Make sure that you have started remote service on you remote device."); + //: INFO ANDROID IOS No paired and reachable device was found, hint that the remote device needs to be actually started for this feature. + return qsTr("No paired and activated remote device was detected. Make sure that you have started the remote service on your remote device."); } else { return ""; } } titleText: (foundSelectedReader ? + //: LABEL DESKTOP_QML qsTr("Determine card") : + //: LABEL DESKTOP_QML qsTr("Establish connection") - ) + settingsModel.translationTrigger + ) + SettingsModel.translationTrigger subTitleText: { - settingsModel.translationTrigger + SettingsModel.translationTrigger if (!visible) { return ""; } else if (!!NumberModel.inputError) { return NumberModel.inputError; } else if (ApplicationModel.extendedLengthApdusUnsupported) { + //: INFO ANDROID IOS The device does not support Extended Length and can not be used as card reader. qsTr("Your remote device does not meet the technical requirements (Extended Length not supported)."); } else if (NumberModel.pinDeactivated) { + //: INFO ANDROID IOS The online authentication is disabled and needs to be enabled by the authorities. return qsTr("The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function."); } else { + //: INFO ANDROID IOS The connection to the smartphone was established, the id card may be inserted. return qsTr("Connected to %1. Please insert your ID card.").arg(RemoteServiceModel.connectedServerDeviceNames); } } diff --git a/resources/qml/Governikus/Workflow/+mobile/TextCircle.qml b/resources/qml/Governikus/Workflow/+mobile/TextCircle.qml index 3b86cb0..3fc61d6 100644 --- a/resources/qml/Governikus/Workflow/+mobile/TextCircle.qml +++ b/resources/qml/Governikus/Workflow/+mobile/TextCircle.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 import QtQuick.Controls 2.3 @@ -6,7 +10,7 @@ import Governikus.Style 1.0 Item { property alias text: t.text - height: Utils.dp(50) + height: 50 width: height BusyIndicator { @@ -22,19 +26,19 @@ Item { id: rec border.width: 1 border.color: Constants.blue - color: parent.state === "active" ? Constants.blue : "white" + color: parent.state === "active" ? Style.color.accent : Constants.white height: parent.state === "active" ? parent.height : parent.height / 2 width: height radius: width * 0.5 anchors.centerIn: parent } - Text { + GText { id: t anchors.centerIn: rec font.bold: parent.state === "active" - font.pixelSize: rec.height / 3 - color: parent.state === "active" ? "white" : Constants.blue + textStyle: Style.text.normal + color: parent.state === "active" ? Constants.white : Style.color.accent } state:"inactive" diff --git a/resources/qml/Governikus/Workflow/+mobile/Workflow.qml b/resources/qml/Governikus/Workflow/Workflow.qml similarity index 53% rename from resources/qml/Governikus/Workflow/+mobile/Workflow.qml rename to resources/qml/Governikus/Workflow/Workflow.qml index cc3d0ed..2934fd2 100644 --- a/resources/qml/Governikus/Workflow/+mobile/Workflow.qml +++ b/resources/qml/Governikus/Workflow/Workflow.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtQuick 2.10 QtObject { diff --git a/resources/qml/main.qml b/resources/qml/main.qml deleted file mode 100644 index 9ec7fb7..0000000 --- a/resources/qml/main.qml +++ /dev/null @@ -1,4 +0,0 @@ -import "main.qml" - -main { -} diff --git a/resources/sonar-project.properties.in b/resources/sonar-project.properties.in index 768df57..c0e27c9 100644 --- a/resources/sonar-project.properties.in +++ b/resources/sonar-project.properties.in @@ -1,4 +1,4 @@ -sonar.host.url=https://sonar.governikus.de +sonar.host.url=https://sonar.govkg.de sonar.projectKey=ausweisapp2:default sonar.projectName=AusweisApp2 @@ -9,9 +9,10 @@ sonar.sources=src sonar.sourceEncoding=UTF-8 sonar.language=cpp +sonar.c.file.suffixes=.c sonar.cpp.file.suffixes=.cpp,.h sonar.objc.file.suffixes=.m,.mm sonar.cfamily.build-wrapper-output=@PROJECT_BINARY_DIR@ sonar.cfamily.gcov.reportsPath=@PROJECT_BINARY_DIR@/Testing/CoverageInfo -sonar.exclusions=src/external/**,utils/**,**/CMakeFiles/* +sonar.exclusions=src/external/**,utils/**,**/CMakeFiles/*,**/*.java diff --git a/resources/statemachine.sh.in b/resources/statemachine.sh.in index 568b961..d402dd6 100755 --- a/resources/statemachine.sh.in +++ b/resources/statemachine.sh.in @@ -5,7 +5,7 @@ s/[[:alnum:]]+\.setInitialState\(s([[:alnum:]]+)\)\;/[*] --> \1/p s/setInitialState\(s([[:alnum:]]+)\)\;/[*] --> \1/p s/auto[[:space:]]sFinal[[:space:]]=[[:space:]]addState\(\)\;/state Final #DarkSeaGreen/p s/[[:alnum:]]+\-.addTransition\(s([[:alnum:]]+)\,[[:space:]]+\&[[:alnum:]]+\:\:fire([[:alnum:]]+)\,[[:space:]]+s([[:alnum:]]+)\)\;/\1 --> \3 : \2/p -s/connect\(s([[:alnum:]]+)\,[[:space:]]+\&[[:alnum:]]+\:\:fire([[:alnum:]]+)\,[[:space:]]+this\,[[:space:]]+\&[[:alnum:]]+\:\:fire([[:alnum:]]+)\)\;/state \3 #DarkSeaGreen\n \1 --> \3 : \2/p +s/connect\(s([[:alnum:]]+)\,[[:space:]]+\&[[:alnum:]]+\:\:fire([[:alnum:]]+)\,[[:space:]]+this\,[[:space:]]+\&[[:alnum:]]+\:\:fire([[:alnum:]]+)\)\;/state \3 #DarkSeaGreen\'$'\n'' \1 --> \3 : \2/p s/auto[[:space:]]+([[:alnum:]]+)[[:space:]]+=\s+addAndConnectState\(\);/\1 --> [*]/p ' @@ -19,9 +19,9 @@ function createImage { rm $2.uml } -createImage @PROJECT_SOURCE_DIR@/src/core/states/CompositeStateSelectCard.cpp @PROJECT_BINARY_DIR@/uml_CompositeStateSelectCard createImage @PROJECT_SOURCE_DIR@/src/core/states/CompositeStatePace.cpp @PROJECT_BINARY_DIR@/uml_CompositeStatePace createImage @PROJECT_SOURCE_DIR@/src/core/states/CompositeStateProcessCvcsAndSetRights.cpp @PROJECT_BINARY_DIR@/uml_CompositeStateProcessCvcsAndSetRights +createImage @PROJECT_SOURCE_DIR@/src/core/states/CompositeStateTrustedChannel.cpp @PROJECT_BINARY_DIR@/uml_CompositeStateTrustedChannel createImage @PROJECT_SOURCE_DIR@/src/core/controller/ChangePinController.cpp @PROJECT_BINARY_DIR@/uml_ChangePinController createImage @PROJECT_SOURCE_DIR@/src/core/controller/SelfAuthController.cpp @PROJECT_BINARY_DIR@/uml_SelfAuthController createImage @PROJECT_SOURCE_DIR@/src/core/controller/AuthController.cpp @PROJECT_BINARY_DIR@/uml_AuthController diff --git a/resources/translations/ausweisapp2_de.ts b/resources/translations/ausweisapp2_de.ts index 0b9574d..5bce47b 100644 --- a/resources/translations/ausweisapp2_de.ts +++ b/resources/translations/ausweisapp2_de.ts @@ -16,16 +16,38 @@ OK - AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Ministry of the Interior, Building and Community. - Die AusweisApp2 ist ein Produkt der Governikus GmbH & Co. KG - im Auftrag des Bundesministeriums des Innern, für Bau und Heimat. + AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Office for Information Security. + Die AusweisApp2 ist ein Produkt der Governikus GmbH & Co. KG - im Auftrag des Bundesamtes für Sicherheit in der Informationstechnik. AdditionalResultsItem Additional results: + LABEL ANDROID_TABLET IOS_TABLET Weitere Ergebnisse: + + Additional results in other categories: %1. Click here to remove filter. + Weitere Ergebnisse in anderen Kategorien: %1. Klicken Sie hier, um den Filter zu entfernen. + + + Additional results in other categories: + LABEL DESKTOP_QML + Weitere Ergebnisse in anderen Kategorien: + + + Show + Anzeigen + + + %1 additional results in other categories + %1 Ergebnisse in anderen Kategorien + + + Click to remove category filter and show additional results. + Klicken Sie hier, um den Filter zu entfernen. + AppQtMainWidget @@ -161,14 +183,58 @@ AvailableDevicesListDelegate - Unsupported - Nicht unterstützt + Great quality + LABEL ANDROID IOS + Gute Qualität + + + Bad quality + Schlechte Qualität + + + Device %1 is available for pairing + Gerät %1 kann gekoppelt werden + + + + BaseConfirmationPopup + + Ok + LABEL ALL_PLATFORMS + Ok + + + Cancel + LABEL ALL_PLATFORMS + Abbrechen + + + + BaseHistoryView + + History + INFO ANDROID IOS + Verlauf + + + Currently there are no history entries. + INFO ANDROID IOS No authentication history, placeholder text. + Derzeit gibt es keine Einträge im Verlauf. + + + + BaseProviderView + + No match found + LABEL IOS_PHONE ANDROID_PHONE Der in das Suchfeld eingegebene String erzielte kein Ergebnis + Keine Übereinstimmung gefunden BluetoothWorkflow Enable Bluetooth + LABEL ANDROID IOS Bluetooth aktivieren @@ -177,6 +243,7 @@ Bluetooth is not supported by your device. + INFO ANDROID IOS Bluetooth is not available to AA2, the user is requested to use NFC instead. Ihr Gerät unterstützt kein Bluetooth. @@ -185,6 +252,7 @@ Bluetooth is switched off. + INFO ANDROID IOS Bluetooth is available but not active. Bluetooth ist deaktiviert. @@ -193,26 +261,32 @@ No paired and activated Bluetooth device was detected. The AusweisApp2 needs access to your location in order to discover available devices. You can grant this permission after clicking the continue button. - Es konnte kein gekoppeltes, eingeschaltetes Bluetooth-Kartenlesegerät erkannt werden. Zur Erkennung benötigt die AusweisApp2 Zugriff auf Ihren Standort. Nach Klicken auf den Fortsetzen-Knopf können Sie die benötigte Freigabe erteilen. + INFO ANDROID IOS Bluetooth is active but the AA2 does not have the location permission which is required to find the bluetooth reader. The user is asked to give the permission to the AA2 after pressing the "ok" button. + Es konnte kein gekoppelter, eingeschalteter Bluetooth-Kartenleser erkannt werden. Zur Erkennung benötigt die AusweisApp2 Zugriff auf Ihren Standort. Nach Klicken auf den Fortsetzen-Knopf können Sie die benötigte Freigabe erteilen. An error occured while connecting to your bluetooth device. Try to pair your device in the system settings and restart the app. - Beim Verbindungsaufbau zum Bluetooth-Kartenlesegerät ist ein Fehler aufgetreten. Bitte koppeln Sie das Gerät über die Einstellungen des Systems und starten Sie die App erneut. + INFO ANDROID IOS The connection to the bluetooth reader failed. + Beim Verbindungsaufbau zum Bluetooth-Kartenleser ist ein Fehler aufgetreten. Bitte koppeln Sie das Gerät über die Einstellungen des Systems und starten Sie die App erneut. Establish connection + INFO ANDROID IOS Status message while connecting to the bluetooth reader - Header Verbindung wird hergestellt Determine card + INFO ANDROID IOS Status message while the connection to the bluetooth has been established and an id card needs to be inserted - Header Ermittle Ausweis Search card reader... - Suche Kartenlesegerät... + LABEL ANDROID IOS INFO ANDROID IOS Status message while the connection to the bluetooth reader is being established. + Suche Kartenleser... Please insert your ID card. + INFO ANDROID IOS Status message after the connection to the bluetooth reader is established and an id card needs to be inserted. Bitte legen Sie Ihren Ausweis ein. @@ -277,6 +351,13 @@ Abbrechen + + CardReaderView + + After connecting a new card reader it may take a few seconds to recognize the driver. It may be necessary to restart your system after installing the driver. Only supported and connected card reader are shown here. %1 + Nachdem ein neuer Kartenleser angeschlossen worden ist, kann es einige Sekunden dauern bis der Treiber erkannt wird. Unter Umständen kann ein Neustart Ihres Betriebssystems notwendig sein. Es werden hier nur unterstützte und angeschlossene Kartenleser angezeigt. %1 + + Category @@ -308,88 +389,128 @@ CertificateDescriptionPage Provider Information + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS Anbieterinformationen Close + LABEL DESKTOP_QML Schließen + + Self-authentication data view + Datenansicht der Selbstauskunft + + + This is the self-authentication data view of the AusweisApp2. + Dies ist die Datenansicht der Selbstauskunft der AusweisApp2. + ChangePinController You may now remove your ID card from the device. + INFO DESKTOP_QML Changing the PIN was successful; hint that the id card may now be removed from the card reader. +---------- +INFO ANDROID IOS Hint that the id card may be removed from the card reader since the PIN was changed successfully. Sie können nun Ihr Ausweisdokument vom Gerät entfernen. + + Weak NFC signal + INFO IOS The NFC signal is weak or unstable. The scan is stopped with this information in the iOS dialog. + Schwacher NFC-Empfang + ChangePinView PIN Management + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS PIN-Verwaltung Change PIN + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS PIN ändern Please wait a moment... + INFO DESKTOP_QML Processing screen text while the card communication is running after the PIN has been entered during PIN change process. +---------- +INFO ANDROID IOS Loading screen during PIN change process, data communcation is currently ongoing. Message is usually not visible since the password handling with basic reader is handled by EnterPasswordView. +---------- +INFO ANDROID IOS Generic progress message during PIN change process. Bitte warten Sie einen Moment... The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. + INFO ANDROID IOS The card communcation was aborted, the online identification functionality is deactivated and needs to be actived by the authorities. Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. Please observe the display of your card reader. - Bitte beachten Sie die Anzeige Ihres Kartenlesegeräts. + INFO ANDROID IOS Either an comfort card reader or smartphone-as-card-reader is used, the user needs to react to request on that device. + Bitte beachten Sie die Anzeige Ihres Kartenlesers. You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card. + INFO ANDROID IOS The wrong PIN was entered twice, the next attempt requires additional verifcation via CAN. Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie vorher Ihre 6-stellige Zugangsnummer eingeben. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking. + INFO ANDROID IOS The PIN (including the CAN) was entered wrongfully three times, the PUK is required to unlock the id card. Sie haben Ihre PIN dreimal falsch eingegeben. Ihre PIN ist jetzt gesperrt. Zum Entsperren geben Sie bitte Ihre PUK ein. Weak NFC signal. Please reposition your card. - Schwacher NFC-Empfang. Bitte korrigieren Sie die Position Ihres Ausweises. + INFO ANDROID IOS The NFC signal is weak or unstable, the user is asked to change the card's position to (hopefully) reduce the distance to the NFC chip. + Schwacher NFC-Empfang. Bitte korrigieren Sie die Position Ihres Ausweisdokuments. Retry + LABEL ANDROID IOS Erneut versuchen + + Weak NFC signal. +Please reposition your card. + INFO DESKTOP_QML The NFC signal is weak or unstable, the user is asked to change the card's position to (hopefully) reduce the distance to the NFC chip. + Schwacher NFC-Empfang. +Bitte korrigieren Sie die Position Ihres Ausweises. + + + Your ID card is unblocked. You now have three more tries to change your PIN + INFO DESKTOP_QML The ID card has just been unblocked and the user can now continue with their PIN change. +---------- +INFO ANDROID IOS The ID card has just been unblocked and the user can now continue with their PIN change. + Ihr Ausweisdokument wurde entsperrt. Sie haben nun drei weitere Versuche um Ihre PIN zu ändern + ChangePinViewContent PIN Management + LABEL ANDROID IOS PIN-Verwaltung You have the opportunity to change your transport PIN into a personal PIN. You can also change the PIN at any time or unblock the PIN using the personal unblocking key (PUK). The transport PIN and the PUK can be found in the letter sent to you by your competent authority. + LABEL ANDROID IOS Hier haben Sie die Möglichkeit, Ihre Transport-PIN in eine persönliche PIN zu ändern. Zudem können Sie jederzeit Ihre persönliche PIN ändern oder eine Blockierung mit Hilfe der Entsperrnummer (PUK) aufheben. Sie finden Ihre Transport-PIN und die PUK in dem Schreiben, das Sie nach Beantragung Ihres Ausweisdokuments von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde erhalten haben. Change PIN now + LABEL ANDROID IOS Jetzt PIN ändern - - ConfirmationPopup - - Cancel - Abbrechen - - - - ContinueButton - - Continue - Fortsetzen - - CredentialDialog @@ -421,8 +542,21 @@ DataGroup No data requested + LABEL DESKTOP_QML +---------- +LABEL ANDROID +---------- +LABEL IOS_TABLET Keine Daten erforderlich + + selected + ausgewählt + + + not selected + nicht ausgewählt + DetailDialog @@ -447,54 +581,90 @@ - DeveloperSettingsWidget + DeveloperSettings - Self authentication test URI: - Test URI für die Selbstauskunft: + Developer options + LABEL DESKTOP_QML + Entwickleroptionen + + Test PKI + LABEL DESKTOP_QML + Test-PKI + + + Developer mode + LABEL DESKTOP_QML + Entwicklermodus + + + The developer mode is aimed at integrators / developers for new service applications. For this reason, the developer mode works only in the test PKI. By activating the developer mode, some safety tests are deactivated. This means that the authentication process continues although the AusweisApp2 would usually abort the process with an error message when used in normal operation mode. Information on the disregarded error in the developer mode is displayed in the attached window below the AusweisApp2. + LABEL DESKTOP_QML Only visible when the user activates the developer mode in the settings. + Der Entwicklermodus richtet sich an Integratoren / Entwickler für neue Dienste. Aus diesem Grund funktioniert der Entwicklermodus lediglich in der Test-PKI. Durch Aktivierung des Entwicklermodus werden einige Sicherheitsprüfungen abgestellt. Die Authentisierung wird auch dann weitergeführt, wenn die AusweisApp2 im Normalbetrieb die Authentisierung mit einer Fehlermeldung abbrechen würde. Der übergangene Fehler im Entwicklermodus wird im angehängten Fenster unterhalb der AusweisApp2 angezeigt. + + + + DeveloperSettingsWidget use verwenden + + Self-authentication test URI: + Test URI für die Selbstauskunft: + DeveloperView Developer options + LABEL ALL_PLATFORMS Entwickleroptionen Test environment + LABEL ALL_PLATFORMS Testumgebung - - Use the test environment during a selfauthentication - Benutze die Test-Umgebung während der Selbstauskunft - Developer Mode + LABEL ALL_PLATFORMS Entwicklermodus Use a more tolerant mode + LABEL ALL_PLATFORMS Benutze einen toleranten Modus Change the layout style + LABEL ALL_PLATFORMS Ändere den Style des Layouts iOS + LABEL ALL_PLATFORMS iOS Android + LABEL ALL_PLATFORMS Android Tablet, Android + LABEL ALL_PLATFORMS Tablet, Android + + Use the test environment during a self-authentication + LABEL ALL_PLATFORMS + Benutze die Test-Umgebung während der Selbstauskunft + + + Developer mode + Entwicklermodus + DiagnosisDialog @@ -511,150 +681,412 @@ Schließen + + DiagnosisView + + Diagnosis view + Anzeige der Diagnose + + + This is the diagnosis view of the AusweisApp2. + Dies ist die Anzeige der Diagnose der AusweisApp2. + + + Diagnosis + LABEL DESKTOP_QML + Diagnose + + + Save diagnosis to textfile + Speichere Diagnose in Textdatei + + + Save to file + LABEL DESKTOP_QML + Speichern unter + + EditRights You are about to identify yourself towards the following service provider: + LABEL DESKTOP_QML +---------- +LABEL ANDROID_PHONE +---------- +LABEL ANDROID_TABLET +---------- +LABEL IOS_PHONE +---------- +LABEL IOS_TABLET Sie möchten sich bei folgendem Diensteanbieter ausweisen: Purpose for reading out requested data + LABEL DESKTOP_QML +---------- +LABEL ANDROID_PHONE +---------- +LABEL ANDROID_TABLET +---------- +LABEL IOS_PHONE +---------- +LABEL IOS_TABLET Zweck des Auslesevorgangs Service provider + LABEL DESKTOP_QML +---------- +LABEL ANDROID_PHONE +---------- +LABEL ANDROID_TABLET +---------- +LABEL IOS_PHONE +---------- +LABEL IOS_TABLET Diensteanbieter more... + LABEL DESKTOP_QML mehr... The following data will be transferred to the service provider when you enter the PIN: + LABEL DESKTOP_QML +---------- +LABEL ANDROID_PHONE +---------- +LABEL ANDROID_TABLET +---------- +LABEL IOS_PHONE +---------- +LABEL IOS_TABLET Folgende Daten Ihres Ausweises werden nach Eingabe der PIN ausgelesen und an den Diensteanbieter übermittelt: Transactional information + LABEL DESKTOP_QML +---------- +LABEL ANDROID_PHONE +---------- +LABEL ANDROID_TABLET +---------- +LABEL IOS_PHONE +---------- +LABEL IOS_TABLET Transaktionsinformationen Required Data + LABEL DESKTOP_QML +---------- +LABEL ANDROID_PHONE +---------- +LABEL ANDROID_TABLET +---------- +LABEL IOS_PHONE +---------- +LABEL IOS_TABLET Erforderliche Daten Optional Data + LABEL DESKTOP_QML +---------- +LABEL ANDROID_PHONE +---------- +LABEL ANDROID_TABLET +---------- +LABEL IOS_PHONE +---------- +LABEL IOS_TABLET Optionale Daten - Identify now - Jetzt ausweisen + Identify + LABEL ANDROID_PHONE +---------- +LABEL ANDROID_TABLET +---------- +LABEL IOS_PHONE +---------- +LABEL IOS_TABLET + Ausweisen - Identify - Ausweisen + Edit rights view + Anzeige zum Ändern der Berechtigungen + + + This is the edit rights view of the AusweisApp2. + Dies ist die Anzeige zum Ändern der Berechtigungen der AusweisApp2. + + + Click for more information about the service provider + Klicke hier für mehr Informationen zum Diensteanbieter + + + Proceed to %1 entry + LABEL DESKTOP_QML %1 can be CAN or PIN +---------- +LABEL ANDROID_PHONE %1 can be CAN or PIN +---------- +LABEL ANDROID_TABLET %1 can be CAN or PIN +---------- +LABEL IOS_PHONE %1 can be CAN or PIN +---------- +LABEL IOS_TABLET %1 can be CAN or PIN + Weiter zur %1-Eingabe - EnterPinView + EnterPasswordView + + Enter %1 view. You can start to enter the number. + Anzeige der %1-Eingabe. Sie können nun Ihre Ziffern eingeben. + + + This is the enter password view of the AusweisApp2. + Dies ist die Anzeige der Passworteingabe der AusweisApp2. + + + Attempts + LABEL DESKTOP_QML + Versuche + + + Remaining attempts: %1 + LABEL DESKTOP_QML + Verbleibende Versuche: %1 + + + %1-Entry + LABEL DESKTOP_QML %1 can be "PIN, CAN, PUK or UNKNOWN" + %1-Eingabe + The entered PIN does not match the new PIN. Please correct your input. + INFO DESKTOP_QML The changed PIN was entered wrongfully during the confirmation process. +---------- +INFO ANDROID IOS The changed PIN was entered wrongfully during confirmation. Die eingegebene PIN stimmt nicht mit Ihrer neuen PIN überein. Bitte korrigieren Sie Ihre PIN-Eingabe. - Please enter the six-digit card access number. You can find the card access number on the front of the ID card. - Bitte geben Sie Ihre 6-stellige Zugangsnummer ein. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. + Please enter your 6-digit PIN. Use the keyboard or numpad. + INFO DESKTOP_QML The AA2 expects a PIN with 6 digit which can be entered via the physical or onscreen keyboard. + Bitte geben Sie Ihre 6-stellige PIN ein. Benutzen Sie dazu die Tastatur oder den Ziffernblock. + + + More information + Mehr Informationen + + + Please enter the 6-digit CAN you can find on the front of your ID card. + INFO DESKTOP_QML The user is required to enter the 6 digit CAN. + Bitte geben Sie Ihre 6-stellige CAN ein. Diese befindet sich auf der Vorderseite Ihres Ausweises. You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card. + INFO DESKTOP_QML The PIN was entered wrongfully twice, this may have happened during an earlier session of the AA2, too. The user needs to enter the CAN for additional verification. +---------- +INFO ANDROID IOS The wrong PIN was entered twice, the third attempt requires the CAN for additional verification, hint where the CAN is found. Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie vorher Ihre 6-stellige Zugangsnummer eingeben. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. - You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking. - Sie haben Ihre PIN dreimal falsch eingegeben. Ihre PIN ist jetzt gesperrt. Zum Entsperren geben Sie bitte Ihre PUK ein. + The online identification function is blocked. Please use your personal unblocking key (PUK) to unblock your ID card. + INFO DESKTOP_QML The PUK is required to unlock the id card. + Die Online-Ausweisfunktion ist blockiert. Die Blockierung können Sie mit Ihrer Entsperrnummer (PUK) aufheben. Please enter a new 6-digit PIN of your choice. + INFO DESKTOP_QML A new 6-digit PIN needs to be supplied. +---------- +INFO ANDROID IOS A new 6-digit PIN needs to be supplied. Geben Sie nun bitte eine neue 6-stellige PIN Ihrer Wahl ein. Please enter your new 6-digit PIN again. + INFO DESKTOP_QML The new PIN needs to be entered again for verification. +---------- +INFO ANDROID IOS The new PIN needs to be confirmed. Wiederholen Sie bitte Ihre neue 6-stellige PIN. - Enter the pairing code shown on your other device to use it as a card reader. - Geben Sie den Kopplungscode, der auf Ihrem anderen Gerät angezeigt wird, ein, um dieses als Kartenlesegerät verwenden zu können. - - - Please enter your personal PIN. - Geben Sie bitte Ihre persönliche PIN ein. - - - Please enter your transport PIN. - Geben Sie bitte Ihre Transport-PIN ein. + Unknown password type: + INFO DESKTOP_QML Error message during PIN/CAN/PUK input procedure, the requested password type is unknown; internal error. + Unbekannter Passwort-Typ: Your PIN has 6 digits? - Ihre PIN ist 6-stellig? + LABEL DESKTOP_QML Button, mit dem der Benutzer eine TransportPIN-Änderung starten kann. +---------- +LABEL ANDROID IOS Button, mit dem der Benutzer eine TransportPIN-Änderung starten kann. + Ist Ihre PIN 6-stellig? Your PIN has 5 digits? - Ihre PIN ist 5-stellig? + Ist Ihre PIN 5-stellig? + + + Please enter the six-digit card access number. You can find the card access number on the front of the ID card. + INFO ANDROID IOS The CAN needs to be entered in CAN-allowed mode, hint where the CAN can be found. + Bitte geben Sie Ihre 6-stellige Zugangsnummer ein. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. + + + You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking. + INFO ANDROID IOS The PUK is required to unlock the id card since the wrong PIN entered three times. + Sie haben Ihre PIN dreimal falsch eingegeben. Ihre PIN ist jetzt gesperrt. Zum Entsperren geben Sie bitte Ihre PUK ein. + + + Please enter your transport PIN. + INFO ANDROID IOS The transport PIN is required by AA2, it needs to be change to an actual PIN. + Geben Sie bitte Ihre Transport-PIN ein. + + + Enter the pairing code shown on your other device to use it as a card reader. + INFO ANDROID IOS The pairing code for the smartphone is required. + Geben Sie den Kopplungscode, der auf Ihrem anderen Gerät angezeigt wird, ein, um dieses als Kartenleser verwenden zu können. + + + Please enter your PIN. + LABEL ANDROID IOS + Geben Sie bitte Ihre PIN ein. + + + Please start the remote service in order to use your smartphone as a card reader with AusweisApp2. + Bitte starten sie den Fernzugriff um Ihr Smartphone als Kartenleser in der AusweisApp2 zu nutzen. + + + Please enter your 5-digit Transport PIN. Use the keyboard or numpad. + INFO DESKTOP_QML The AA2 expects a PIN with 5 digit which can be entered via the physical or onscreen keyboard. + Bitte geben Sie Ihre 5-stellige Transport-PIN ein. Benutzen Sie dazu die Tastatur oder den Ziffernblock. Feedback Rate AusweisApp2 + LABEL ANDROID IOS Bewerten Sie die AusweisApp2 Help & Feedback + LABEL ANDROID IOS Hilfe & Feedback FAQ + LABEL ANDROID IOS FAQ Do you have questions how to use AusweisApp2? + LABEL ANDROID IOS Haben Sie Fragen zur Nutzung der AusweisApp2? - - https://www.ausweisapp.bund.de/en/qa/frequently-asked-questions/ - https://www.ausweisapp.bund.de/fragen-und-antworten/haeufig-gestellte-fragen/ - Support + LABEL ANDROID IOS Support You need further help? + LABEL ANDROID IOS Benötigen Sie weitere Hilfe? - - https://www.ausweisapp.bund.de/en/qa/support/ - https://www.ausweisapp.bund.de/fragen-und-antworten/support/ - Please rate us in the Google Play Store. + LABEL ANDROID IOS Bewerten Sie die AusweisApp2. Report error + LABEL ANDROID IOS Melden Sie einen Fehler You found a bug? Please tell us, so we can fix it. + LABEL ANDROID IOS Sie haben einen Fehler gefunden? Teilen Sie ihn uns mit, damit wir ihn beheben können. Show log + LABEL ANDROID IOS Protokoll anzeigen You can view the logs of the AusweisApp2 here. + LABEL ANDROID IOS Hier können Sie das Protokoll der AusweisApp2 einsehen. + + https://www.ausweisapp.bund.de/en/qa/frequently-asked-questions/ + LABEL ANDROID IOS + https://www.ausweisapp.bund.de/fragen-und-antworten/haeufig-gestellte-fragen/ + + + https://www.ausweisapp.bund.de/en/qa/support/ + LABEL ANDROID IOS + https://www.ausweisapp.bund.de/fragen-und-antworten/support/ + + + + GSwitch + + Switch + Wechseln + + + + GeneralSettings + + Language selection + LABEL DESKTOP_QML + Sprachauswahl + + + German + Deutsch + + + Set language to german + Verwende deutsche Sprache + + + English + Englisch + + + Set language to english + Verwende englische Sprache + + + UI settings + LABEL DESKTOP_QML + Einstellungen der grafischen Oberfläche + + + Switch back to old UI + LABEL DESKTOP_QML + Alte Oberfläche verwenden + + + Behavior + LABEL DESKTOP_QML + Verhalten + + + Auto start AusweisApp2 after boot + LABEL DESKTOP_QML + AusweisApp2 automatisch beim Hochfahren starten + + + Close after authentication + LABEL DESKTOP_QML + Nach Authentisierung schließen + GeneralSettingsWidget @@ -722,107 +1154,294 @@ use on screen password Bildschirmtastatur verwenden + + Try out the new beta UI: + Probieren Sie die neue grafische Oberfläche: + + + Switch UI + Grafische Oberfläche wechseln + + + + GeneralWorkflow + + General workflow view + Allgemeiner Fortschritt + + + This is the general workflow view of the AusweisApp2. + Dies ist die allgemeine Fortschrittsansicht der AusweisApp2. + + + Change PIN + LABEL DESKTOP_QML + PIN ändern + + + Identify + Ausweisen + + + The device %1 was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself. + INFO DESKTOP_QML The paired devices was removed since it did not respond to connection attempts. It needs to be paired again if it should be used as card reader. + Das Gerät %1 wurde entkoppelt, da es nicht auf Verbindungsversuche reagiert hat. Versuchen Sie das Gerät erneut zu koppeln, wenn Sie es zur Authentisierung verwenden möchten. + + + Attempts + LABEL DESKTOP_QML + Versuche + + + Remaining attempts: + Verbleibende Versuche: + + + Step %1 of 3 + Schritt %1 von 3 + + + Establish connection + LABEL DESKTOP_QML + Verbindung wird hergestellt + + + Information + LABEL DESKTOP_QML + Information + + + Please insert the card into the device + INFO DESKTOP_QML The AA2 is waiting for an id card to be inserted into the card reader. + Bitte legen Sie die Karte auf den Kartenleser + + + Searching for card reader + INFO DESKTOP_QML AA2 is waiting for the card reader or the id card. + Suche nach Kartenleser + + + The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. + INFO DESKTOP_QML The online authentication feature of the card is disabled and needs to be activated by the authorities. + Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. + + + Please observe the display of your card reader. + INFO DESKTOP_QML The card reader is a comfort reader with its own display, the user is requested to pay attention to that display (instead of the AA2). + Bitte beachten Sie die Anzeige Ihres Kartenlesers. + + + Please place the smartphone on the card + INFO DESKTOP_QML The AA2 is waiting for the smartphone to be placed on the id. + Bitte platzieren Sie das Smartphone auf der Karte + + + Please place the smartphone on the card or insert the card into the device + INFO DESKTOP_QML The AA2 is waiting for an id card to be inserted into the card reader (or smartphone for that matter). + Bitte platzieren Sie das Smartphone auf der Karte oder legen Sie die Karte in den Kartenleser ein + + + The used card reader does not meet the technical requirements (Extended Length not supported). + ERROR DESKTOP_QML + Der verwendete Kartenleser erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). + + + + Hamburger + + Show navigation + Zeige Navigation + + + Back + Zurück + + + Cancel + Abbrechen + + + + HistoryListItem + + Click to view details of history entry. + Zeige Details des Verlaufseintrags. + + + today + LABEL ANDROID IOS + heute + + + yesterday + gestern + + + dddd + dddd + + + dd.MM.yyyy + dd.MM.yyyy + + + Tap for more details + LABEL ANDROID IOS + Berühren Sie hier für mehr Details + HistoryListViewDelegate - today - heute - - - yesterday - gestern - - - dddd - dddd - - - dd.MM.yyyy - dd.MM.yyyy - - - Tap for more details - Berühren Sie hier für mehr Details + Delete history entry: %1 + INFO IOS Accessible name for the trash icon of a history entry. + Lösche Verlaufseintrag: %1 - HistoryListViewDelegateContent + HistoryRemovalTimePeriodControl - today - heute + Time period + LABEL DESKTOP_QML + Zeitspanne - yesterday - gestern + Past hour + LABEL DESKTOP_QML + Letzte Stunde - dddd - dddd + Past day + LABEL DESKTOP_QML + Letzter Tag - dd.MM.yyyy - dd.MM.yyyy + Past week + LABEL DESKTOP_QML + Letzte Woche - Tap for more details - Berühren Sie hier für mehr Details + Last four weeks + LABEL DESKTOP_QML + Letzte vier Wochen + + + All history + LABEL DESKTOP_QML + Gesamter Zeitraum HistoryView History + LABEL DESKTOP_QML Verlauf Currently there are no history entries. + INFO DESKTOP_QML No authentication history, placeholder text. Derzeit gibt es keine Einträge im Verlauf. Delete all + LABEL IOS Lösche alle - Please confirm that you want to delete your complete history. - Bitte bestätigen Sie, dass der komplette Verlauf gelöscht werden soll. + History view + Verlaufsanzeige + + + This is the history view of the AusweisApp2. + Dies ist die Verlaufsanzeige der AusweisApp2. + + + Delete history? + INFO DESKTOP_QML Header of the confirmation dialog to clear the entire authentication history. + Verlauf löschen? + + + Please confirm that you want to delete your history entries. + INFO DESKTOP_QML Content of the confirmation dialog to clear the entire authentication history. + Bitte bestätigen Sie das Löschen der Verlaufseinträge. + + + Removed %1 entries from the history. + INFO DESKTOP_QML Feedback how many history entries were removed. + Es wurden %1 Einträge aus dem Verlauf entfernt. + + + Clear history + LABEL DESKTOP_QML + Lösche Verlauf + + + Save to pdf + LABEL DESKTOP_QML + Speichern als PDF + + + today + heute + + + yesterday + gestern + + + dddd + dddd + + + dd.MM.yyyy + dd.MM.yyyy HistoryViewConfirmationPopup Delete history + LABEL ANDROID IOS Verlauf löschen - - Cancel - Abbrechen - Delete + LABEL ANDROID IOS Löschen - Removed %1 entries from the history. - Es wurden %1 Einträge aus dem Verlauf entfernt. + Please confirm that you want to delete your complete history. + LABEL ANDROID IOS Confirmaton popup to clear all history entries. + Bitte bestätigen Sie, dass der komplette Verlauf gelöscht werden soll. HistoryViewDetails Provider Information + LABEL ANDROID IOS Anbieterinformationen Provider name + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS Anbieter Purpose + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS Zweck Date + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS Datum @@ -831,27 +1450,34 @@ Requested data + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS Angeforderte Daten Terms of usage + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS Nutzungsbedingungen + + Details for history entry + Details des Verlaufseintrags + HistoryViewTitleBarControls History enabled + LABEL ANDROID IOS Verlauf aktiviert History disabled Verlauf deaktiviert - - Please confirm that you want to delete your complete history. - Bitte bestätigen Sie, dass der komplette Verlauf gelöscht werden soll. - HistoryWidget @@ -904,175 +1530,279 @@ IdentifyController You may now remove your ID card from the device. + INFO DESKTOP_QML The authentication process is completed, the id card may be removed from the card reader. +---------- +INFO ANDROID IOS The authentication process is completed, the id card may (and should) be removed from the card reader. Sie können nun Ihr Ausweisdokument vom Gerät entfernen. + + Weak NFC signal + INFO IOS The NFC signal is weak or unstable. The scan is stopped with this information in the iOS dialog. + Schwacher NFC-Empfang + IdentifyView Identify + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS Ausweisen Authentication in progress + INFO DESKTOP_QML Header of the progress information during the authentication process. +---------- +INFO ANDROID IOS Header of the progress status message during the authentication process. Authentisierung wird durchgeführt Acquiring provider certificate + INFO ANDROID IOS Header of the progress status message during the authentication process. Lade Berechtigungszertifikat herunter Please wait a moment... + INFO DESKTOP_QML Second line text if a basic card reader is used and data is exchanged with the card/server in the background. Is not actually visible since the basic reader password handling is done by EnterPasswordView. +---------- +INFO DESKTOP_QML Generic progress status message during authentication. +---------- +INFO ANDROID IOS Second line text if a basic card reader is used and background communication with the card/server is running. Is not actually visible since the basic reader password handling is done by EnterPasswordView. +---------- +INFO ANDROID IOS Generic status message during the authentication process. Bitte warten Sie einen Moment... The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. + INFO DESKTOP_QML The online authentication feature of the id card is deactivated and needs to be activated by the local authorities. +---------- +INFO ANDROID IOS The online authentication feature of the id card is disabled and needs to be actived the be authorities. Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. Please observe the display of your card reader. - Bitte beachten Sie die Anzeige Ihres Kartenlesegeräts. + INFO ANDROID IOS The card reader requests the user's attention. + Bitte beachten Sie die Anzeige Ihres Kartenlesers. You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card. + INFO ANDROID IOS The PIN was entered wrongfully two times, the third attempts requires additional CAN verification, hint where the CAN is found. Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie vorher Ihre 6-stellige Zugangsnummer eingeben. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. - - Service provider is being verified - Diensteanbieter wird geprüft - - - Card is being verified - Karte wird geprüft - - - Reading data - Daten werden gelesen - - - Sending data to service provider - Sende Daten an den Dienstanbieter - - - Preparing results - Bereite Ergebnisse vor - No network connectivity + INFO DESKTOP_QML Header of the message that no network connection is present during the authentication procedure. +---------- +LABEL ANDROID IOS Keine Netzwerkverbindung Please enable the network interface or cancel the workflow. + INFO DESKTOP_QML Content of the message that no network connection is present during the authentication procedure. +---------- +INFO ANDROID IOS No network connection, the user needs to active the network interface or abort the procedure. Bitte stellen Sie eine Internetverbindung her oder beenden Sie den Vorgang. - - Identify view - Ausweisen-Ansicht - - - This is the identify view of the AusweisApp2. - Dies ist die Ausweisen-Ansicht der AusweisApp2. - Weak NFC signal. Please reposition your card. + INFO ANDROID IOS The NFC signal is weak, by repositioning the card the signal might improve. Schwacher NFC-Empfang. Bitte korrigieren Sie die Position Ihres Ausweises. Change transport PIN + LABEL ANDROID IOS Transport-PIN ändern Change PIN + LABEL ANDROID IOS PIN ändern Retry + LABEL ANDROID IOS Erneut versuchen Cancel authentication process + LABEL ANDROID IOS Beende Ausweisvorgang You have to change your transport PIN into a personal PIN to use the online ID function. You are currently leaving the started process and are forwarded to the PIN management. Please restart the desired process after the PIN has been changed. + INFO DESKTOP_QML The user clicked that the current PIN has 5 digits (transport PIN) which needs to be changed to a 6-digit PIN. The current process will be aborted and needs to be restarted *manually* by the user. +---------- +INFO ANDROID IOS The user clicked that the current PIN has 5 digits (transport PIN), it needs to be changed to an ordinary 6 digit PIN. The current process needs to be restarted *manually* by the user. Sie müssen Ihre fünfstellige Transport-PIN zunächst in eine persönliche PIN ändern. Sie sind dabei den aktuell laufenden Vorgang zu verlassen und zur PIN-Verwaltung weitergeleitet zu werden. Starten Sie den gewünschten Vorgang bitte nach der PIN-Änderung erneut. + + Transport PIN + LABEL DESKTOP_QML + Transport PIN + + + Weak NFC signal. +Please reposition your card. + INFO DESKTOP_QML A weak NFC signal was detected since the card communication was aborted. The card's position needs to be adjusted to hopefully achieve better signal strength. + Schwacher NFC-Empfang. +Bitte korrigieren Sie die Position Ihres Ausweises. + - Information + InformationView - Information - Information + Help section + Hilfe + + + This is the help section of the AusweisApp2. + Dies ist die Hilfe der AusweisApp2. + + + Help + LABEL DESKTOP_QML + Hilfe + + + Questions, feedback, and rating + LABEL DESKTOP_QML + Fragen, Rückmeldungen und Bewertungen + + + Questions + LABEL DESKTOP_QML + Fragen + + + https://www.ausweisapp.bund.de/en/qa/frequently-asked-questions/ + https://www.ausweisapp.bund.de/fragen-und-antworten/haeufig-gestellte-fragen/ + + + Report error + LABEL DESKTOP_QML + Melden Sie einen Fehler + + + https://www.ausweisapp.bund.de/en/qa/report-an-error/ + https://www.ausweisapp.bund.de/feedback/melden-sie-einen-fehler/ + + + Rate application + LABEL DESKTOP_QML + Anwendung bewerten + + + https://www.ausweisapp.bund.de/en/qa/evaluate-us/ + https://www.ausweisapp.bund.de/feedback/bewerten-sie-uns/ + + + Setup and manual + LABEL DESKTOP_QML + Einrichtung und Handbuch + + + Setup + LABEL DESKTOP_QML + Einrichtung + + + Online help + LABEL DESKTOP_QML + Online-Hilfe + + + Diagnosis and application log + LABEL DESKTOP_QML + Diagnose- und Anwendungsprotokoll + + + Diagnosis + LABEL DESKTOP_QML + Diagnose + + + Application log + LABEL DESKTOP_QML + Anwendungsprotokoll + + + Version and license information + LABEL DESKTOP_QML + Version und Lizenzinformationen Version information + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS Versionsinformationen + + Software licenses + LABEL DESKTOP_QML + Softwarelizenzen + + + https://www.ausweisapp.bund.de/en/download/windows-and-mac/ + LABEL ANDROID IOS + https://www.ausweisapp.bund.de/download/windows-und-mac/ + + + Information + LABEL ANDROID IOS + Information + Here you can see detailed information about AusweisApp2. + LABEL ANDROID IOS Hier finden Sie detaillierte Informationen zur AusweisApp2. Software license + LABEL ANDROID IOS Softwarelizenz Read the software license text on the application homepage. + LABEL ANDROID IOS Lesen Sie die Softwarelizenz auf der Internetseite der Anwendung. https://www.ausweisapp.bund.de/en/download/android/ + LABEL ANDROID IOS https://www.ausweisapp.bund.de/download/android/ https://www.ausweisapp.bund.de/en/download/ios/ + LABEL ANDROID IOS https://www.ausweisapp.bund.de/download/ios/ - - https://www.ausweisapp.bund.de/en/download/windows-and-mac/ - https://www.ausweisapp.bund.de/download/windows-und-mac/ - KnownDevicesListDelegate - - Available - Verfügbar - - - Available, but unsupported - Verfügbar, aber nicht unterstützt - Last connection: + LABEL ANDROID IOS Letzte Verbindung: - - - Log - Log - Protokoll + Not available + LABEL ANDROID IOS + Nicht verfügbar - Delete - Löschen + Great quality + LABEL ANDROID IOS + Gute Qualität - Log: - Protokoll: + Bad quality + Schlechte Qualität - Please confirm that you want to delete your old logfiles. - Bitte bestätigen Sie, dass alle alten Protokolle gelöscht werden sollen. - - - Delete all - Lösche alle - - - Please confirm that you want to delete the logfile. - Bitte bestätigen Sie, dass das Protokoll gelöscht werden soll. + Device %1 has status %2 + Gerät %1 hat den Status %2 @@ -1098,6 +1828,91 @@ Schließen + + LogTitleBarControls + + Share logfile + Teile Protokolldatei + + + Delete logfile + Lösche Protokolldatei + + + Delete all logfiles + Lösche alle Protokolldateien + + + + LogView + + Logfile viewer + Protokolldatei anzeigen + + + This is the logfile viewer of the AusweisApp2. + Dies ist die Anzeige der Protokolldatei der AusweisApp2. + + + Application log + LABEL DESKTOP_QML + Anwendungsprotokoll + + + Log: + LABEL ANDROID IOS + Protokoll: + + + Save logfile + LABEL DESKTOP_QML + Protokolldatei speichern + + + Delete logfile + LABEL DESKTOP_QML + Lösche Protokolldatei + + + Delete old logfiles + LABEL DESKTOP_QML + Lösche alte Protokolldateien + + + Delete selected logfile + Lösche ausgewählte Protokolldateien + + + Please confirm that you want to delete your old logfiles. + INFO DESKTOP_QML The current/all logfile(s) are about to be removed, user confirmation required. +---------- +INFO ANDROID IOS The current/all logfile(s) are about to be removed, user confirmation required. + Bitte bestätigen Sie, dass alle alten Protokolle gelöscht werden sollen. + + + Please confirm that you want to delete the logfile. + Bitte bestätigen Sie, dass das Protokoll gelöscht werden soll. + + + Log + LABEL ANDROID IOS + Protokoll + + + Delete all + LABEL ANDROID IOS + Lösche alle + + + Delete + LABEL ANDROID IOS + Löschen + + + Select logfile from list. + Wähle Protokolldatei aus. + + MainView @@ -1108,121 +1923,139 @@ This is the main view of the AusweisApp2. Dies ist die Hauptseite der AusweisApp2. - - Identify - Ausweisen - Provider + LABEL DESKTOP_QML Anbieter History + LABEL DESKTOP_QML Verlauf PIN management + LABEL DESKTOP_QML PIN-Verwaltung Settings + LABEL DESKTOP_QML Einstellungen Help + LABEL DESKTOP_QML Hilfe - You can use your ID card anywhere you see this logo. - Überall wo Sie dieses Logo sehen, können Sie Ihr Ausweisdokument einsetzen. - - - Use the button 'See my personal data' to display the data stored on your ID card. An Internet connection is required to display the data. - Über die Schaltfläche 'Meine Daten einsehen' können Sie sich die im Chip Ihres Ausweisdokuments gespeicherten Daten anzeigen lassen. Um die Daten anzeigen zu können, benötigt diese Anwendung eine Internetverbindung. - - - Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed. - Es erfolgt keine Speicherung oder Weiterverarbeitung Ihrer persönlichen Daten. Näheres dazu erfahren Sie in unserer %1. - - - https://www.ausweisapp.bund.de/datenschutz/ - https://www.ausweisapp.bund.de/datenschutz/ - - - data privacy statement - Datenschutzerklärung - - - See my personal data - Meine Daten einsehen + Self-authentication + LABEL DESKTOP_QML + Selbstauskunft MoreView More + LABEL ANDROID IOS DESKTOP_QML Mehr Version information + LABEL ANDROID IOS DESKTOP_QML Versionsinformationen FAQ + LABEL ANDROID IOS DESKTOP_QML FAQ - - https://www.ausweisapp.bund.de/en/qa/frequently-asked-questions/ - https://www.ausweisapp.bund.de/fragen-und-antworten/haeufig-gestellte-fragen/ - Support + LABEL ANDROID IOS DESKTOP_QML Support - - https://www.ausweisapp.bund.de/en/qa/support/ - https://www.ausweisapp.bund.de/fragen-und-antworten/support/ - Rate app + LABEL ANDROID IOS DESKTOP_QML Bewerten - - https://www.ausweisapp.bund.de/en/qa/evaluate-us/ - https://www.ausweisapp.bund.de/fragen-und-antworten/bewerten-sie-uns/ - Software license + LABEL ANDROID IOS DESKTOP_QML Softwarelizenz https://www.ausweisapp.bund.de/en/download/android/ + LABEL ANDROID IOS DESKTOP_QML https://www.ausweisapp.bund.de/download/android/ https://www.ausweisapp.bund.de/en/download/ios/ + LABEL ANDROID IOS DESKTOP_QML https://www.ausweisapp.bund.de/download/ios/ https://www.ausweisapp.bund.de/en/download/windows-and-mac/ + LABEL ANDROID IOS DESKTOP_QML https://www.ausweisapp.bund.de/download/windows-und-mac/ - Configure remote service - Fernzugriff konfigurieren + History + LABEL ANDROID IOS DESKTOP_QML + Verlauf Developer options + LABEL ANDROID IOS DESKTOP_QML Entwickleroptionen Show log + LABEL ANDROID IOS DESKTOP_QML Protokoll anzeigen Tutorial + LABEL ANDROID IOS DESKTOP_QML Tutorial + + https://www.ausweisapp.bund.de/en/qa/frequently-asked-questions/ + LABEL ANDROID IOS DESKTOP_QML + https://www.ausweisapp.bund.de/fragen-und-antworten/haeufig-gestellte-fragen/ + + + https://www.ausweisapp.bund.de/en/qa/support/ + LABEL ANDROID IOS DESKTOP_QML + https://www.ausweisapp.bund.de/fragen-und-antworten/support/ + + + Set language to german + Verwende deutsche Sprache + + + Set language to english + Verwende englische Sprache + + + Report error + LABEL ANDROID IOS DESKTOP_QML + Melden Sie einen Fehler + + + + NavigationButton + + Continue + LABEL DESKTOP_QML + Fortsetzen + + + Back + Zurück + NavigationView @@ -1238,6 +2071,10 @@ History Verlauf + + Remote + Fernzugriff + PIN Management PIN-Verwaltung @@ -1252,7 +2089,7 @@ Smartphone as card reader - Smartphone als Kartenlesegerät + Smartphone als Kartenleser Developer options @@ -1266,23 +2103,38 @@ Tutorial Tutorial + + PIN + PIN + + + Set language to german + Verwende deutsche Sprache + + + Set language to english + Verwende englische Sprache + NfcWorkflow NFC is not supported by your device. + INFO ANDROID IOS AA2 can't use NFC on this device, suggest to use bluetooth instead. Ihr Gerät unterstützt kein NFC. - Please try Bluetooth. - Bitte versuchen Sie Bluetooth. + You require an additional 'Bluetooth card reader' or an additional 'smartphone as card reader' to use the online identification function with this device. + Sie können die Online-Ausweisfunktion aber mit einem separaten Bluetooth-Leser oder einem anderen Smartphone als Kartenleser nutzen. NFC is switched off. + INFO ANDROID IOS NFC is available but needs to be activated in the settings of the smartphone. NFC ist nicht aktiv. Go to NFC settings + LABEL DESKTOP_QML Zu den NFC Einstellungen @@ -1295,22 +2147,97 @@ Your device does not meet the technical requirements (Extended Length not supported). You require an additional 'Bluetooth card reader' or an additional 'smartphone as card reader' to use the online identification function with this device. - Ihr Gerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length). Sie können die Online-Ausweisfunktion daher mit diesem Gerät nur über einen separaten Bluetooth-Leser oder mit einem separaten Smartphone als Kartenlesegerät nutzen. + INFO ANDROID IOS The NFC interface does not meet the minimum requirements, using a bluetooth reader or a differnt smarthpone is suggested. + Ihr Gerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length). Sie können die Online-Ausweisfunktion daher mit diesem Gerät nur über einen separaten Bluetooth-Leser oder mit einem separaten Smartphone als Kartenleser nutzen. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. + INFO ANDROID IOS The online authentication feature is disabled and needs to be activated by the authorities. Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. Please place your device<br/>on your ID card. + INFO ANDROID IOS The id card may be inserted, the authentication process may be started. Bitte platzieren Sie Ihr Gerät<br/>über Ihrem Personalausweis. + + Start NFC scan + NFC-Scan starten + + + NFC scan is not running. + INFO ANDROID IOS NFC is available but needs to be activated in the settings of the smartphone. + Der NFC-Scan ist nicht aktiv. + + + Please start the NFC scan. + Bitte starten Sie den NFC-Scan. + + + + NumberPad + + Numberpad to enter the password + Ziffernblock Passworteingabe + + + Delete last digit + Lösche letzte Ziffer + + + Submit + Absenden + + + + PasswordInfoView + + Password information + Passwort Information + + + This is the password information section of the AusweisApp2. + Dies ist die Informationsansicht zu Passwörtern der AusweisApp2. + + + %1 information + %1 can be "PIN, CAN, PUK or UNKNOWN" + Informationen zur %1 + + + The card access number (CAN) is only required if you have already entered the PIN incorrectly twice. In order to prevent a third incorrect entry and thus the blocking of the ID card without your consent, the CAN is also requested at this point. The CAN is a six-digit number that can be found on the front of the ID card. It is located at the bottom right next to the validity date (marked in red). + INFO DESKTOP_QML Description text of CAN + Die Zugangsnummer (CAN) wird nur dann verlangt, wenn Sie die PIN bereits zwei Mal falsch eingegeben haben. Um zu verhindern, dass ohne Ihre Zustimmung eine dritte Fehleingabe und somit die Sperrung des Ausweises erfolgen kann, wird an dieser Stelle zusätzlich die CAN abgefragt. Die CAN ist eine sechsstellige Zahl, die auf der Vorderseite des Personalausweises zu finden ist. Sie steht rechts unten neben dem Gültigkeitsdatum (rot markiert). + + + The personal unblocking key (PUK) is only required if you entered the the wrong PIN three times. The online eID function is blocked at this time. The PUK is a ten-digit number you received with the letter sent to you by your competent authority (marked in red). Please note that you can only use the PUK to unblock the PIN entry. If you have forgotten your PIN, you can have a new PIN set at your competent authority. + INFO DESKTOP_QML Description text of PUK + Die Entsperrnummer (PUK) wird nur dann verlangt, wenn Sie die PIN bereits drei Mal falsch eingegeben haben. Zu diesem Zeitpunkt ist die Online-Ausweisfunktion gesperrt. Die PUK ist eine zehnstellige Zahl die Sie mit dem Schreiben bekommen haben, das Sie nach Beantragung Ihres Ausweisdokuments von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde erhalten haben (rot markiert). Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihre PIN-Eingabe entsperren können. Sollten Sie Ihre PIN vergessen haben, können Sie von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde eine neue PIN setzen lassen. + + + The personal identification number (PIN) is required for every use of the online eID function. You can change it anytime and indefinitely if you know your valid PIN. For your 6-digit PIN choose a combination of numbers, that is not easy to guess, neither "123456" nor your birth date, or any other numbers printed on the ID card. If you are no longer aware of your valid PIN, you will need to contact the authority responsible for issuing your identification document to renew your PIN. + +When changing the PIN for the first time, please use your 5-digit transport PIN. You will find the transport PIN in the letter you received from the authority responsible for issuing your identification document (marked in red) after you have applied for your identity card. + +Please note that you can not use the online eID function with your 5-digit transport PIN. A change to a 6-digit PIN is mandatory. + INFO DESKTOP_QML Description text of PIN + Die Geheimnummer (PIN) benötigen Sie bei jeder Nutzung der Online-Ausweisfunktion. Sie können diese jederzeit und unbegrenzt oft ändern, wenn Ihnen Ihre gültige PIN bekannt ist. Wählen Sie für Ihre 6-stellige PIN eine Zahlenkombination, die nicht leicht zu erraten ist, also weder "123456", noch Ihr Geburtsdatum oder andere Zahlen, die auf dem Ausweisdokument aufgedruckt sind. Soweit Ihnen Ihre gültige PIN nicht mehr bekannt ist, müssen Sie zur Neuvergabe einer persönlichen PIN die für die Ausgabe Ihres Ausweisdokuments zuständige Behörde aufsuchen. + +Bei der erstmaligen PIN-Änderung verwenden Sie bitte Ihre 5-stellige Transport-PIN. Sie finden die Transport-PIN in dem Schreiben, das Sie nach Beantragung Ihres Ausweisdokuments von der für die Ausgabe Ihres Ausweisdokuments zuständigen Behörde erhalten haben (rot markiert). + +Bitte beachten Sie, dass Sie die Online-Ausweisfunktion mit Ihrer 5-stellige Transport-PIN nicht nutzen können. Eine Änderung in eine 6-stellige PIN ist zwingend erforderlich. + + + You may use your smartphone as a card reader with AusweisApp2. The smartphone needs to feature a supported NFC chipset and both devices, your smartphone and this machine, need to be connected to the same WiFi network.<br><br>Please make sure that the remote service is running on your smartphone. Start the pairing process by clicking the "Start pairing" button and enter the shown 4-digit PIN. + INFO DESKTOP_QML Description text of SaC pairing + Sie können Ihr Smartphone als Kartenleser in der AusweisApp2 verwenden. Das Smartphone muss einen unterstützen NFC-Chip verbaut haben und beide Geräte, sowohl das Smartphone als auch dieser Computer, müssen mit dem selben WLAN-Netz verbunden sein.<br><br>Bitte stellen Sie sicher, dass der Fernzugriff in der App des Smartphones aktiviert ist. Starten sie den Kopplungsprozess indem Sie den "Koppeln"-Knopf anklicken und die vom Smartphone dargestellte 4-stellige PIN eingeben. + PinSettingsWidget Please pay attention to the display of your card reader. - Bitte beachten Sie die Anzeige auf Ihrem Kartenlesegerät. + Bitte beachten Sie die Anzeige auf Ihrem Kartenleser. <h4>PIN successfully changed</h4> @@ -1322,7 +2249,7 @@ If not, you can now remove your ID card form the card reader. Klicken Sie auf "PIN ändern", wenn Sie noch einmal Ihre PIN neu setzen wollen. -Anderenfalls können Sie nun Ihr Ausweisdokument vom Kartenlesegerät entfernen. +Anderenfalls können Sie nun Ihr Ausweisdokument vom Kartenleser entfernen. <html> @@ -1331,8 +2258,8 @@ Anderenfalls können Sie nun Ihr Ausweisdokument vom Kartenlesegerät entfernen. </p> </html> <html> -<h4>Es wurde kein Kartenlesegerät gefunden. Bitte stellen Sie sicher, dass ein Kartenlesegerät angeschlossen ist.</h4> -<p>Wenn Sie Hilfe bei der Einrichtung Ihres Kartenlesegerät benötigen, klicken Sie auf "Diagnose".</p> +<h4>Es wurde kein Kartenleser gefunden. Bitte stellen Sie sicher, dass ein Kartenleser angeschlossen ist.</h4> +<p>Wenn Sie Hilfe bei der Einrichtung Ihres Kartenleser benötigen, klicken Sie auf "Diagnose".</p> </html> @@ -1357,7 +2284,7 @@ Anderenfalls können Sie nun Ihr Ausweisdokument vom Kartenlesegerät entfernen. Please make sure that only one card reader with an ID card on it is connected to your computer. - Bitte stellen Sie sicher, dass an Ihrem Computer nur ein Kartenlesegerät mit aufliegendem Ausweisdokument angeschlossen ist. + Bitte stellen Sie sicher, dass an Ihrem Computer nur ein Kartenleser mit aufliegendem Ausweisdokument angeschlossen ist. <html> @@ -1383,10 +2310,6 @@ Bei der erstmaligen PIN-Änderung geben Sie bitte in das Feld "Aktuelle PIN You have entered the wrong PIN two times. For a third attempt you first have to enter your six-digit card access number. You can find your card access number on the front side of your ID card next to the date of expiry. On the electronic residence permit the card access number is printed above your signature. Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie zunächst Ihre 6-stellige Zugangsnummer eingeben. Die Zugangsnummer finden Sie beim Personalausweis auf der Vorderseite rechts neben dem letzten Tag der Gültigkeitsdauer. Beim elektronischen Aufenthaltstitel finden Sie die Zugangsnummer über Ihrer Unterschrift. - - <h4>PUK entry successful</h4><p>Your ID card is unblocked. You now have three more tries to change your PIN.</p> - <h4>Eingabe der PUK erfolgreich</h4><p>Ihr Ausweis ist entsperrt. Sie haben nun erneut drei Versuche, Ihre PIN zu ändern.</p> - Current PIN / Transport PIN: Aktuelle PIN / Transport-PIN: @@ -1429,19 +2352,19 @@ Bei der erstmaligen PIN-Änderung geben Sie bitte in das Feld "Aktuelle PIN card reader icon - Icon des Kartenlesegeräts + Icon des Kartenlesers no reader icon - Kein Kartenlesegerät Icon + Kein Kartenleser Icon multiple card reader icon - Mehrer Kartenlesegerät Icon + Mehrere Kartenleser Icon deactivated card reader icon - Deaktiverter Kartenlesegerät Icon + Deaktiverter Kartenleser Icon successful PIN change icon @@ -1464,23 +2387,67 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe <h4>Extended Length is not supported.</h4> <p>Your remote reader does not meet the technical requirements (Extended Length not supported).</p> </html> - Ihr entferntes Kartenlesegerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt).< + Ihr entfernter Kartenleser erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt).< open on screen keyboard öffne bildschirmtastatur + + ProgressView + + Progress view + Fortschrittsanzeige + + + This is the progress view of the AusweisApp2. + This ist die Fortschrittsanzeige der AusweisApp2. + + + Step %1 of %2 + Schritt %1 von %2 + + + + ProviderCard + + Provider: + Anbieter: + + + Unknown error + Unbekannter Fehler + + + Provider description: + Anbieterbeschreibung: + + ProviderContactInfo Contact + LABEL DESKTOP_QML +---------- +LABEL ANDROID_TABLET IOS_TABLET Kontakt Unknown + LABEL DESKTOP_QML +---------- +LABEL ANDROID_TABLET IOS_TABLET Unbekannt + + Provider contact information + Kontaktinformationen des Diensteanbieters + + + Contact information of the selected service provider. + Kontaktinformationen des ausgewählten Diensteanbieters. + ProviderContactTab @@ -1493,13 +2460,28 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderDetailButtonBar ONLINE APPLICATION + LABEL ANDROID_TABLET IOS_TABLET Online-Anwendung + + Link to service provider + Link zum Diensteanbieter + + + Clicking this link will open the website of the service provider in your web browser. The URL of the provider is + Durch einen Klick auf diesen Link öffnen Sie die Webseite des Diensteanbieters. Die URL des Anbieters lautet + + + To online application + LABEL DESKTOP_QML + Zur Online-Anwendung + ProviderDetailDescription Description + LABEL ANDROID_TABLET IOS_TABLET Beschreibung @@ -1507,29 +2489,48 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderDetailHistory History + LABEL ANDROID_TABLET IOS_TABLET Verlauf Purpose for reading out requested data + LABEL ANDROID_TABLET IOS_TABLET Zweck des Auslesevorgangs + + List of your past interactions with this provider + Ihre vergangenen Interaktionen mit diesem Diensteanbieter + + + The list is empty, no recorded interaction with this service provider. + Die Liste ist leer, keine Interaktion mit diesem Diensteanbieter aufgezeichnet. + + + Currently there are no history entries. + INFO DESKTOP_QML No authentication history, placeholder text. + Derzeit gibt es keine Einträge im Verlauf. + ProviderDetailHistoryInfo Service provider + LABEL ANDROID_TABLET IOS_TABLET Diensteanbieter Purpose for reading out requested data + LABEL ANDROID_TABLET IOS_TABLET Zweck des Auslesevorgangs Read data + LABEL ANDROID_TABLET IOS_TABLET Daten auslesen Terms of usage + LABEL ANDROID_TABLET IOS_TABLET Nutzungsbedingungen @@ -1537,6 +2538,7 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderDetailHistoryItem today + LABEL ANDROID IOS heute @@ -1553,32 +2555,74 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Touch for more details + LABEL ANDROID IOS Berühren Sie hier für mehr Details + + Service: + LABEL DESKTOP_QML + Dienst: + + + Provider: + LABEL DESKTOP_QML + Anbieter: + + + Click to view details of history entry. + Zeige Details des Verlaufseintrags. + ProviderDetailView DESCRIPTION + LABEL ANDROID_PHONE IOS_PHONE BESCHREIBUNG CONTACT + LABEL ANDROID_PHONE IOS_PHONE KONTAKT Description not available + LABEL ANDROID_PHONE IOS_PHONE Beschreibung nicht verfügbar - Provider details - Anbieterdetails + Provider detail view + Detailansicht des Diensteanbieters + + + This view shows a detailed description of a service provider. + Diese Ansicht zeigt eine detaillierte Beschreibung eines Diensteanbieters. + + + Description + LABEL DESKTOP_QML + Beschreibung + + + Description of the service provider. + Beschreibung des Diensteanbieters. + + + The service provider did not provide a description. + LABEL DESKTOP_QML + Der Diensteanbieter hat keine Beschreibung zur Verfügung gestellt. + + + History + LABEL DESKTOP_QML + Verlauf ProviderHeader To service provider + LABEL ANDROID IOS Zum Anbieter @@ -1586,13 +2630,22 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ProviderInfoSection Touch for more details + LABEL ANDROID IOS Berühren Sie hier für mehr Details See details under "more..." + LABEL DESKTOP_QML Weitere Details unter "mehr..." + + ProviderListItemDelegate + + Open provider details for + Öffne Details vom Diensteanbieter + + ProviderModelItem @@ -1611,9 +2664,14 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Contact Kontakt + + Costs + LABEL DESKTOP_QML + Kosten + - ProviderView + ProviderOverview Provider view Anbieteransicht @@ -1623,35 +2681,81 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Dies ist die Anbieteransicht der AusweisApp2. - Provider - Anbieter + All provider + LABEL DESKTOP_QML + Alle Diensteanbieter Citizen services + LABEL DESKTOP_QML + Bürgerdienste + + + Financials + LABEL DESKTOP_QML + Finanzen + + + Insurances + LABEL DESKTOP_QML + Versicherungen + + + Other services + LABEL DESKTOP_QML + Weitere Dienste + + + + ProviderSectionDelegate + + Click to set category filter to %1 + Klicke hier um den Kategoriefilter auf %1 zu setzen + + + + ProviderView + + Provider + LABEL DESKTOP_QML +---------- +LABEL IOS_TABLET + Diensteanbieter + + + Citizen services + LABEL ANDROID_TABLET +---------- +LABEL IOS_TABLET Bürgerdienste Insurances + LABEL ANDROID_TABLET +---------- +LABEL IOS_TABLET Versicherungen Financials + LABEL ANDROID_TABLET +---------- +LABEL IOS_TABLET Finanzen Other services + LABEL ANDROID_TABLET +---------- +LABEL IOS_TABLET Weitere Services No match found - Kein Ergebnis gefunden - - - - ProviderViewDelegate - - i - i + LABEL ANDROID_TABLET +---------- +LABEL IOS_TABLET Der in das Suchfeld eingegebene String erzielte kein Ergebnis + Keine Übereinstimmung gefunden @@ -1673,6 +2777,25 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Bitte geben Sie Ihre Suche ein + + QObject + + Please describe the error that occurred. + Bitte beschreiben Sie den aufgetretenen Fehler. + + + You may want to attach the logfile which can be saved from the error dialog. + Bitte fügen Sie der E-Mail das Protokoll als Anhang zu. + + + Error code + Fehlernummer + + + Critical errors: + Kritische Fehler: + + RandomPinDialog @@ -1700,7 +2823,8 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ReaderConfigurationInfo Unknown reader - Unbekanntes Kartenlesegerät + LABEL ALL_PLATFORMS + Unbekannter Kartenleser @@ -1718,11 +2842,11 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ReaderDeviceWidget In order to use the online identification function of your ID card you need a separate card reader or a suitable smartphone. The following overview shows the status of a connected card reader or connected smartphone. - Um die Online-Ausweisfunktion des Personalausweises nutzen zu können, benötigen Sie ein separates Kartenlesegerät oder auch ein geeignetes Smartphone. Die nachfolgende Übersicht stellt dar, in welchem Status sich ein angeschlossenes Kartenlesegerät oder verbundenes Smartphone befindet. + Um die Online-Ausweisfunktion des Personalausweises nutzen zu können, benötigen Sie einen separaten Kartenleser oder auch ein geeignetes Smartphone. Die nachfolgende Übersicht stellt dar, in welchem Status sich ein angeschlossener Kartenleser oder verbundenes Smartphone befindet. Smartphone as card reader - Smartphone als Kartenlesegerät + Smartphone als Kartenleser Pair @@ -1734,11 +2858,11 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Card readers - Kartenlesegeräte + Kartenleser After connecting a new card reader it may take a few seconds to recognize the driver. It may be necessary to restart your system after installing the driver. - Nachdem ein neues Kartenlesegerät angeschlossen worden ist, kann es einige Sekunden dauern bis der Treiber erkannt wird. Unter Umständen kann ein Neustart Ihres Betriebssystems notwendig sein. + Nachdem ein neuer Kartenleser angeschlossen worden ist, kann es einige Sekunden dauern bis der Treiber erkannt wird. Unter Umständen kann ein Neustart Ihres Betriebssystems notwendig sein. TextLabel @@ -1756,29 +2880,54 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Geben Sie den 4-stelligen Kopplungscode ein, der auf Ihrem anderen Gerät angezeigt wird. + + RemoteReaderDelegate + + Click to pair + Klicken zum Koppeln + + + + RemoteReaderView + + Paired remote devices + Gekoppelte Netzwerkgeräte + + + Available remote devices + Verfügbare Netzwerkgeräte + + + No devices with enabled remote service were found on the current WiFi network + Im WLAN-Netz wurden keine Geräte mit aktiviertem Fernzugriff gefunden + + + Only devices that are already paired or are connected to the same WiFi network and have the remote service enabled are shown here. + Nur Geräte die bereits gekoppelt wurden, oder sich mit aktiviertem Fernzugriff im selben WLAN-Netz befinden, werden hier angezeigt. + + RemoteServicePairingPopup Pairing code + INFO ANDROID IOS Header of the pairing dialog when using the smartphone as card reader Kopplungscode - - Enter the pairing code shown on your other device to use it as a card reader - Geben Sie den Kopplungscode ein, der auf Ihrem anderen Gerät angezeigt wird, um es als Kartenlesegerät zu verwenden - Enter this code on your other device to use this device as a card reader - Geben Sie diesen Code auf Ihrem anderen Gerät ein, um dieses Gerät als Kartenlesegerät zu verwenden + INFO ANDROID IOS Main text of the pairing dialog when using the smartphone as card reader. + Geben Sie diesen Code auf Ihrem anderen Gerät ein, um dieses Gerät als Kartenleser zu verwenden - Start pairing - Kopplung starten + Click to close dialog + Schließe hiermit den Dialog RemoteServiceSettings Configure remote service + LABEL ANDROID IOS Fernzugriff konfigurieren @@ -1786,18 +2935,22 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe RemoteServiceView Configure local settings + LABEL ANDROID IOS Konfiguriere lokale Einstellungen Pair remote devices - Kopple entferntes Kartenlesegerät + LABEL ANDROID IOS + Kopple entfernten Kartenleser Remote service + LABEL ANDROID IOS Fernzugriff Pairing failed. Please try again to activate pairing on your other device and enter the shown pairing code. + ERROR ANDROID IOS An error occurred while pairing the device. Die Kopplung ist fehlgeschlagen. Bitte starten Sie eine neue Kopplung an Ihrem anderen Gerät und geben den angezeigten Kopplungscode ein. @@ -1805,18 +2958,22 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe RemoteServiceViewLocal Device name + LABEL ANDROID IOS Gerätename Set device name: + LABEL ANDROID IOS Wählen Sie einen Gerätenamen: PIN pad mode + LABEL ANDROID IOS Tastaturmodus Enter PIN on this device + LABEL ANDROID IOS PIN-Eingabe auf diesem Gerät @@ -1824,108 +2981,128 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe RemoteServiceViewRemote Paired devices + LABEL ANDROID IOS Gekoppelte Geräte No device is paired. + LABEL ANDROID IOS Kein Gerät gekoppelt. Available devices + LABEL ANDROID IOS Verfügbare Geräte No new remote reader was found on your network. Make sure that the remote reader functionality in AusweisApp2 on your other device is activated and that your devices are connected to the same network. - Kein entferntes Kartenlesegerät in Ihrem Netzwerk verfügbar. Bitte stellen Sie sicher, dass die Funktion "Fernzugriff" in der AusweisApp2 auf Ihrem anderen Gerät aktiviert ist. Beide Geräte müssen sich im selben Netzwerk befinden. + INFO ANDROID IOS No remote reader was found on the network, both devices need to be connected to the same wifi network. + Kein entfernter Kartenleser in Ihrem Netzwerk verfügbar. Bitte stellen Sie sicher, dass die Funktion "Fernzugriff" in der AusweisApp2 auf Ihrem anderen Gerät aktiviert ist. Beide Geräte müssen sich im selben Netzwerk befinden. Please start pairing mode first. + INFO ANDROID IOS Information dialog that requests the user to start the pairing mode on the smarthpone. Starten Sie den Kopplungsmodus auf Ihrem Smartphone, falls noch nicht geschehen. - OK - OK + Pairing code + LABEL ANDROID IOS + Kopplungscode - Pairing code - Kopplungscode + Pairing mode + INFO ANDROID IOS + Kopplungsmodus RemoteServiceViewStartStop Please start the remote service in order to use your smartphone as a card reader with AusweisApp2. Please note: Both your devices have to be connected to the same WiFi. - Bitte starten Sie den Fernzugriff, damit Sie Ihr Smartphone als Kartenlesegerät für die AusweisApp2 nutzen können. Bitte beachten Sie: dies ist nur möglich, wenn beide Geräte mit demselben WLAN verbunden sind. + INFO ANDROID IOS The remote service is active. Hint that both devices need to be connected to the same network. + Bitte starten Sie den Fernzugriff, damit Sie Ihr Smartphone als Kartenleser für die AusweisApp2 nutzen können. Bitte beachten Sie: dies ist nur möglich, wenn beide Geräte mit demselben WLAN verbunden sind. Enable NFC + LABEL ANDROID IOS NFC aktivieren Stop remote service + LABEL ANDROID IOS Fernzugriff stoppen Start remote service + LABEL ANDROID IOS Fernzugriff starten Start pairing + LABEL ANDROID IOS Kopplung starten Card access in progress + LABEL ANDROID IOS Kartenzugriff - - Please pay attention to the display on your other device %1. - Bitte beachten Sie die Anzeige auf Ihrem anderen Gerät %1. - RemoteWorkflow Continue + LABEL DESKTOP_QML Fortsetzen Pair device + LABEL DESKTOP_QML Gerät koppeln Enable Wifi + LABEL DESKTOP_QML WLAN aktivieren Establish connection + LABEL DESKTOP_QML Verbindung wird hergestellt Your remote device does not meet the technical requirements (Extended Length not supported). - Ihr entferntes Kartenlesegerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). + INFO ANDROID IOS The device does not support Extended Length and can not be used as card reader. + Ihr entfernter Kartenleser erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. + INFO ANDROID IOS The online authentication is disabled and needs to be enabled by the authorities. Die Online-Ausweisfunktion Ihres Ausweisdokumentes ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. Connected to %1. Please insert your ID card. + INFO ANDROID IOS The connection to the smartphone was established, the id card may be inserted. Verbunden mit %1. Bitte legen Sie Ihr Ausweisdokument auf. Determine card + LABEL DESKTOP_QML Ermittle Ausweis The device %1 was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself. + INFO ANDROID IOS The paired smartphone was removed since it did not respond to connection attempts. It needs to be paired again before using it. Das Gerät %1 wurde entkoppelt, da es nicht auf Verbindungsversuche reagiert hat. Versuchen Sie das Gerät erneut zu koppeln, wenn Sie es zur Authentisierung verwenden möchten. To use the remote service WiFi has to be activated. Please activate WiFi in your device settings. + INFO ANDROID IOS The wifi module needs to be enabled in the system settings to use the remote service. Um den Fernzugriff zu nutzen, muss WLAN aktiviert werden. Bitte aktivieren Sie WLAN in Ihren Einstellungen. - No paired and activated remote device was detected. Make sure that you have started remote service on you remote device. + No paired and activated remote device was detected. Make sure that you have started the remote service on your remote device. + INFO ANDROID IOS No paired and reachable device was found, hint that the remote device needs to be actually started for this feature. Kein gekoppeltes und aktiviertes Gerät gefunden. Stellen Sie sicher, dass der Fernzugriff auf Ihrem Gerät gestartet wurde. @@ -1933,6 +3110,7 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ResultView OK + LABEL ANDROID IOS OK @@ -1945,53 +3123,207 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Send log file + LABEL ANDROID IOS Sende Protokoll + + Send email + LABEL DESKTOP_QML + E-Mail senden + SearchBar Search + LABEL DESKTOP_QML +---------- +LABEL IOS Suchen Cancel + LABEL IOS Abbrechen + + Clear + Löschen + + + Enter search string + Sucheingabe + + + Clear search string + Lösche Sucheingabe + SectionSwitch Service + LABEL ANDROID IOS Dienst Pairing + LABEL ANDROID IOS Kopplung Settings + LABEL ANDROID IOS Einstellungen + + SecurityAndPrivacySettings + + History + LABEL DESKTOP_QML + Verlauf + + + Save authentification history + LABEL DESKTOP_QML + Verlauf der Ausweisvorgänge speichern + + + Clear entire history + LABEL DESKTOP_QML + Lösche gesamten Verlauf + + + Onscreen keypad + LABEL DESKTOP_QML + Bildschirmtastatur + + + Use on screen keypad for PIN entry + LABEL DESKTOP_QML + Bildschirmtastatur für die PIN-Eingabe verwenden + + + Shuffle keypad buttons + LABEL DESKTOP_QML + Zufällige Anordnung der Ziffern + + + Software updates + LABEL DESKTOP_QML + Software-Aktualisierungen + + + Check at program start + LABEL DESKTOP_QML + Automatisch bei Programmstart prüfen + + + Check now + LABEL DESKTOP_QML + Jetzt überprüfen + + SelfAuthenticationData Identify + LABEL ANDROID IOS Ausweisen - - Successfull reading data - Daten erfolgreich gelesen - OK + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS OK Read data + LABEL DESKTOP_QML Daten auslesen + + Self-authentication data view + Anzeige der Selbstauskunft + + + This is the self-authentication data view of the AusweisApp2. + Dies ist die Anzeige der Selbstauskunft der AusweisApp2. + + + Successfully read data + INFO DESKTOP_QML Status message that the self authentication successfully completed. + Lesevorgang erfolgreich + + + Read data successfully + INFO ANDROID IOS The self authentication was successfully completed. + Daten erfolgreich ausgelesen + + + + SelfAuthenticationView + + Identify + LABEL DESKTOP_QML + Ausweisen + + + You can use your ID card anywhere you see this logo. + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS + Überall wo Sie dieses Logo sehen, können Sie Ihr Ausweisdokument einsetzen. + + + Use the button 'See my personal data' to display the data stored on your ID card. An Internet connection is required to display the data. + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS + Über die Schaltfläche 'Meine Daten einsehen' können Sie sich die im Chip Ihres Ausweisdokuments gespeicherten Daten anzeigen lassen. Um die Daten anzeigen zu können, benötigt diese Anwendung eine Internetverbindung. + + + Your personal data is neither saved nor processed in any way. Please see our %1 for details on how your personal data is processed. + LABEL ANDROID IOS + Es erfolgt keine Speicherung oder Weiterverarbeitung Ihrer persönlichen Daten. Näheres dazu erfahren Sie in unserer %1. + + + https://www.ausweisapp.bund.de/en/privacy/ + https://www.ausweisapp.bund.de/datenschutz/ + + + data privacy statement + Datenschutzerklärung + + + See my personal data + LABEL DESKTOP_QML +---------- +LABEL ANDROID IOS + Meine Daten einsehen + + + %1 more presses to toggle test PKI. + %1 weitere Klicks um die Test-PKI umzuschalten. + + + Test PKI activated. + Test-PKI aktiviert. + + + Test PKI deactivated. + Test-PKI deaktiviert. + + + https://www.ausweisapp.bund.de/datenschutz/ + https://www.ausweisapp.bund.de/datenschutz/ + + + Click to open link to data privacy statement in browser: %1 + Öffne den Link der Datenschutzerklärung im Browser: %1 + SelfInfoWidget @@ -2019,6 +3351,47 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe eID Logo + + SettingsView + + Settings view + Einstellungsanzeige + + + This is the settings panel of the AusweisApp2. + Dies ist die Einstellungsanzeige der AusweisApp2. + + + Settings + LABEL DESKTOP_QML + Einstellungen + + + Developer options + LABEL DESKTOP_QML + Entwickleroptionen + + + General + LABEL DESKTOP_QML + Allgemein + + + Smartphone as card reader + LABEL DESKTOP_QML + Smartphone als Kartenleser + + + USB card reader + LABEL DESKTOP_QML + USB Kartenleser + + + Security and privacy + LABEL DESKTOP_QML + Sicherheit und Datenschutz + + SettingsWidget @@ -2043,7 +3416,81 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Card Readers - Kartenlesegeräte + Kartenleser + + + + SetupAssistantBinaryDecisionView + + No + LABEL DESKTOP_QML + Nein + + + Yes + LABEL DESKTOP_QML + Ja + + + + SetupAssistantCardReaderView + + Advance + LABEL DESKTOP_QML + Fortfahren + + + + SetupAssistantView + + Setup Assistant + LABEL DESKTOP_QML + Einrichtungsassistent + + + Welcome to the AusweisApp2. Please take a few moments to setup the environment to your needs. Every decision you make can later be changed in the settings menu. + INFO DESKTOP_QML Welcome message when starting the setup assistant. + Willkommen in der AusweisApp2. Bitte nehmen Sie sich einen Moment Zeit, um die App nach Ihren Wünschen anzupassen. Jede Einstellung kann später wieder im Einstellungsmenü geändert werden. + + + Do you want to save a history of performed authentications? + INFO DESKTOP_QML Question if the authentication history shall be stored. + Möchten Sie den Verlauf durchgeführter Authentifizierungen speichern? + + + The following data is saved: authentication date, service provider contact data, read data. + INFO DESKTOP_QML Information text which data is stored in the history record. + Folgende Daten werden gespeichert: Authentisierungsdatum, Kontaktdaten des Diensteanbieters, Art der ausgelesenen Daten. + + + History Setting + LABEL DESKTOP_QML + Verlaufseinstellungen + + + Continue + LABEL DESKTOP_QML + Fortsetzen + + + Do you want to change your transport PIN to a personal PIN now? + INFO DESKTOP_QML Inquiry message if the 5 digit transport PIN should be changed to an ordinary PIN (now). + Möchten Sie jetzt Ihre Transport PIN in eine persönliche PIN ändern? + + + This process can always be started from the main menu. The online-ID function is only usable with a personal PIN. + INFO DESKTOP_QML Hint that this change may be carried out form the main menu as well and that it is required to use the online authentication feature of the id card. + Dieser Prozess kann jederzeit aus dem Hauptmenü heraus gestartet werden. Die Online-Ausweisfunktion ist nur mit einer persönlichen PIN nutzbar. + + + Transport PIN + LABEL DESKTOP_QML + Transport PIN + + + You have completed the setup of the AusweisApp2 successfully. + INFO DESKTOP_QML Success message after completing the setup assistant. + Sie haben die Einrichtung der AusweisApp2 erfolgreich abgeschlossen. @@ -2089,43 +3536,63 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe StoreFeedbackPopup Would you like to rate this app? + INFO ANDROID Header of the app rating popup. Möchten Sie diese App bewerten? - - No, Thanks - Nein danke - Rate + LABEL ANDROID Bewerten We would be very grateful if you could leave a rating on the Google Play Store! + INFO ANDROID Content of the app rating popup. Wir würden uns sehr über eine Bewertung im Google Play Store freuen! + + No, thanks + LABEL ANDROID + Nein, danke + + + + TabbedReaderView + + Card Readers + LABEL DESKTOP_QML + Kartenleser + + + Smartphone as card reader + Smartphone als Kartenleser + + + USB card reader + USB Kartenleser + + + Please start pairing mode first. + LABEL DESKTOP_QML + Starten Sie den Kopplungsmodus auf Ihrem Smartphone, falls noch nicht geschehen. + TechnologySwitch NFC + LABEL ANDROID IOS NFC WiFi + LABEL ANDROID IOS WLAN Bluetooth + LABEL ANDROID IOS Bluetooth - - Use WiFi card reader instead<br/>of Bluetooth card reader - WLAN Kartenlesegerät anstelle <br/>vom Bluetooth Kartenlesegerät verwenden - - - Use Bluetooth card reader instead<br/>of remote card reader - Bluetooth Kartenlesegerät anstelle <br/>vom WLAN Kartenlesegerät verwenden - TitleBar @@ -2139,6 +3606,7 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Start + LABEL DESKTOP_QML Start @@ -2149,30 +3617,50 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Help Hilfe + + Notifications + Benachrichtungen + - TitleBarAction + TitleBarNavigation Cancel + LABEL ANDROID IOS Abbrechen Edit + LABEL ANDROID IOS Bearbeiten - < back - < Zurück + Back + LABEL ANDROID IOS + Zurück + + + + ToggleableOption + + is enabled + ist aktiviert + + + is disabled + ist deaktiviert TutorialFooter Fold in + LABEL ANDROID IOS Einklappen Quit tutorial + LABEL ANDROID IOS Tutorial beenden @@ -2180,10 +3668,12 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe TutorialHow You can find a list of compatible NFC-capable smartphones here: + LABEL ANDROID IOS Eine Liste kompatibler NFC-fähiger Smartphones finden Sie hier: Direct connection via NFC chip + LABEL ANDROID IOS Direktes Auslesen über NFC-Chip @@ -2192,50 +3682,57 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Smartphone as card reader + LABEL ANDROID IOS Smartphone als Kartenleser App on computer <b>without</b> NFC chip + LABEL ANDROID IOS App auf Computer <b>ohne</b> NFC-Chip - - Android smartphone <b>with</b> NFC chip as card reader - Android-Telefon <b>mit</b> NFC-Chip als Kartenlesegerät - App on tablet or smartphone <b>without</b> NFC chip + LABEL ANDROID IOS App auf Tablet oder Telefon <b>ohne</b> NFC-Chip Using a bluetooth card reader - Mit Bluetooth-Kartenlesegerät + LABEL ANDROID IOS + Mit Bluetooth-Kartenleser App on smartphone or tablet + LABEL ANDROID IOS App auf Telefon oder Tablet Bluetooth card reader - Bluetooth-Kartenlesegerät + LABEL ANDROID IOS + Bluetooth-Kartenleser Another tip + LABEL ANDROID IOS Noch ein Tipp For lenghty forms, e.g. a BAföG application, we recommend you to use the AusweisApp2 on a computer... + LABEL ANDROID IOS Für aufwändigere Formulare wie z.B. den BAföG-Antrag empfehlen wir Ihnen die AusweisApp2 am Computer zu verwenden... Filling long forms is no fun on a smartphone! + LABEL ANDROID IOS Längere Anträge ausfüllen macht am Mobiltelefon keinen Spaß! ... and to use a smartphone to communicate with your ID card. A USB reader is of course also an alternative. - ... und zum Lesen des Ausweises das Smartphone als Kartenlesegerät zu nutzen. Alternativ geht natürlich auch ein USB-Lesegerät. + LABEL ANDROID IOS + ... und zum Lesen des Ausweises das Smartphone als Kartenleser zu nutzen. Alternativ geht natürlich auch ein USB-Lesegerät. https://www.ausweisapp.bund.de/mobile-geraete/ + LABEL ANDROID IOS https://www.ausweisapp.bund.de/mobile-geraete/ @@ -2243,18 +3740,40 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Wie kann ich die AusweisApp2 auf meinem Smartphone nutzen? - Many Android devices can access the id card via the NFC interface. - Viele Android-Geräte mit NFC können den Ausweis auslesen. + The AusweisApp2 offers the following options to access your id card: + LABEL ANDROID IOS + Die AusweisApp2 bietet zum Auslesen die folgenden Möglichkeiten: - The AusweisApp2 offers the following options to access your id card: - Die AusweisApp2 bietet zum Auslesen die folgenden Möglichkeiten: + How can I use the AusweisApp2 on my iPhone? + INFO ANDROID IOS + Wie kann ich die AusweisApp2 auf meinem iPhone nutzen? + + + Many iPhones (iPhone 7 and newer) can access the id card via the built-in NFC interface. + INFO ANDROID IOS + Viele iPhones (iPhone 7 und neuer) können direkt über die im Gerät verbaute NFC-Schnittstelle auf die Online-Ausweisfunktion des Ausweises zugreifen. + + + Many Android devices can access the id card via the built-in NFC interface. + Viele Android-Geräte können direkt über die im Gerät verbaute NFC-Schnittstelle auf die Online-Ausweisfunktion des Ausweises zugreifen. + + + App on iPhone <b>with</b> NFC chip as card reader + LABEL ANDROID IOS + App auf iPhone <b>mit</b> NFC-Chip als Kartenleser + + + Smartphone <b>with</b> NFC chip as card reader + LABEL ANDROID IOS + Smartphone <b>mit</b> NFC-Chip als Kartenleser TutorialImportant Please exchange your + LABEL ANDROID IOS Bitte tauschen Sie Ihre @@ -2263,22 +3782,27 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe 5 digits long + LABEL ANDROID IOS 5-stellige transport PIN + LABEL ANDROID IOS Transport-PIN with a personal + LABEL ANDROID IOS durch eine persönliche 6 digits long PIN + LABEL ANDROID IOS 6-stellige PIN before you use the online ID function! + LABEL ANDROID IOS bevor Sie die Online-Ausweisfunktion verwenden! @@ -2287,22 +3811,27 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe Choose for this purpose the menu entry PIN management + LABEL ANDROID IOS Hierzu den Menüpunkt PIN-Verwaltung auswählen Let's go + LABEL ANDROID IOS Los geht's Do you still have questions? + LABEL ANDROID IOS Oder haben Sie noch Fragen? You can read our <b>FAQs</b> or <b>write</b> to us... + LABEL ANDROID IOS Dann können Sie in unsere <b>FAQs</b> schauen oder uns <b>schreiben</b>... www.ausweisapp.bund.de + LABEL ANDROID IOS www.ausweisapp.bund.de @@ -2315,69 +3844,88 @@ Bitte beachten Sie, dass Sie mit Ihrer PUK lediglich Ihren Online-Ausweis entspe ... or click this button to change your PIN right now: + LABEL ANDROID IOS ... oder auf diesen Button klicken um die PIN jetzt zu ändern: Change PIN + LABEL ANDROID IOS PIN ändern Later you can also change your personal PIN here + LABEL ANDROID IOS Hier können Sie auch später jederzeit Ihre persönliche PIN ändern - - qrc:///images/tutorial/screenshot_pin_management_menu_en.png - qrc:///images/tutorial/screenshot_pin_management_menu_de.png - https://www.youtube.com/watch?v=wZglRda5Y60&index=4&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm https://www.youtube.com/watch?v=wZglRda5Y60&index=4&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm Learn more about this in the YouTube video + LABEL ANDROID IOS Sie können mehr dazu im YouTube-Video erfahren - - The transport PIN is send to you by the Bundesdruckerei via mail. - Die Transport-PIN wird Ihnen von der Bundesdruckerei per Brief zugesandt. - You can always access this tutorial again from the side bar. Sie können dieses Tutorial jederzeit wieder von der Seitenleiste aus aufrufen. + + The transport PIN is sent to you by the Bundesdruckerei via mail. + LABEL ANDROID IOS + Die Transport-PIN wird Ihnen von der Bundesdruckerei per Brief zugesandt. + + + qrc:///images/tutorial/screenshot_pin_management_menu_%1_en.png + LABEL ANDROID IOS + qrc:///images/tutorial/screenshot_pin_management_menu_%1_de.png + + + You can always access this tutorial again from the "More" section in the menu bar. + LABEL ANDROID IOS + Sie können dieses Tutorial jederzeit wieder über den Bereich "Mehr" aus aufrufen. + TutorialReaderMethodBluetooth Tutorial: Bluetooth + LABEL ANDROID IOS Tutorial: Bluetooth Using a bluetooth card reader - Mit Bluetooth-Kartenlesegerät + LABEL ANDROID IOS + Mit Bluetooth-Kartenleser App on smartphone or tablet + LABEL ANDROID IOS App auf Telefon oder Tablet Bluetooth card reader - Bluetooth-Kartenlesegerät + LABEL ANDROID IOS + Bluetooth-Kartenleser You need a suitable card reader if you want to use the bluetooth connection. - Die Bluetooth-Verbindung setzt voraus, dass ein geeignetes Kartenlesegerät vorhanden ist. + LABEL ANDROID IOS + Die Bluetooth-Verbindung setzt voraus, dass ein geeigneter Kartenleser vorhanden ist. Set the card reader visible first... - Zunächst das Kartenlesegerät sichtbar machen... + LABEL ANDROID IOS + Zunächst den Kartenleser sichtbar machen... ... and then pair it with your device. + LABEL ANDROID IOS ...und mit dem Gerät koppeln. Click the link on the website of the service provider. + LABEL ANDROID IOS Link auf Webseite des Diensteanbieters klicken. @@ -2387,38 +3935,42 @@ klicken. The App opens automatically. + LABEL ANDROID IOS Die App öffnet sich automatisch. The AusweisApp2 will display who wants to access which data. + LABEL ANDROID IOS Ihnen wird angezeigt, wer welche Ihrer Daten abfragen will. Start the process with a click on: + LABEL ANDROID IOS Starten Sie den Vorgang mit: - Identify now - Jetzt ausweisen - - - qrc:///images/tutorial/screenshot_choose_reader_en.png - qrc:///images/tutorial/screenshot_choose_reader_de.png + Proceed to PIN entry + LABEL ANDROID IOS + Weiter zur PIN-Eingabe Tap on Bluetooth + LABEL ANDROID IOS Tap auf Bluetooth Insert card into card reader + LABEL ANDROID IOS Karte ins Lesegerät stecken ... and confirm the displayed information. + LABEL ANDROID IOS ...und angezeigte Informationen bestätigen. Enter + LABEL ANDROID IOS Eingeben @@ -2427,25 +3979,33 @@ klicken. 6 digits long PIN + LABEL ANDROID IOS 6-stellige PIN now on the card reader! - jetzt auf dem Kartenlesegerät! + LABEL ANDROID IOS + jetzt auf dem Kartenleser! enter on the card reader! - am Kartenlesegerät eingeben! + am Kartenleser eingeben! This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand. + LABEL ANDROID IOS Funktioniert nur, wenn Sie Ihre 5-stellige Transport-PIN bereits in Ihre persönliche 6-stellige PIN geändert haben. + + qrc:///images/tutorial/screenshot_choose_reader_%1_en.png + qrc:///images/tutorial/screenshot_choose_reader_%1_de.png + TutorialReaderMethodFooter Back + LABEL ANDROID IOS Zurück @@ -2453,10 +4013,12 @@ klicken. TutorialReaderMethodNfc Tutorial: NFC + LABEL ANDROID IOS Tutorial: NFC Direct connection via NFC chip + LABEL ANDROID IOS Direktes Auslesen über NFC Chip @@ -2465,6 +4027,7 @@ klicken. Click link on the website of the service provider. + LABEL ANDROID IOS Link auf Webseite des Diensteanbieters klicken. @@ -2473,19 +4036,23 @@ klicken. The App opens automatically. + LABEL ANDROID IOS Die App öffnet sich automatisch. The AusweisApp2 will display who wants to access which data. + LABEL ANDROID IOS Ihnen wird angezeigt, wer welche Ihrer Daten abfragen will. Start the process with a click on: + LABEL ANDROID IOS Starten Sie den Vorgang mit: - Identify now - Jetzt ausweisen + Proceed to PIN entry + LABEL ANDROID IOS + Weiter zur PIN-Eingabe ... and place the id card flat onto the NFC interface. @@ -2493,14 +4060,17 @@ klicken. Do not move device or id card! + LABEL ANDROID IOS Gerät und Ausweis nicht bewegen! The correct position is specific for your device... + LABEL ANDROID IOS Die genaue Position hängt von Ihrem Gerät ab... https://www.ausweisapp.bund.de/mobile-geraete/ + LABEL ANDROID IOS https://www.ausweisapp.bund.de/mobile-geraete/ @@ -2509,6 +4079,7 @@ klicken. Enter + LABEL ANDROID IOS Eingeben @@ -2517,10 +4088,12 @@ klicken. 6 digits long PIN + LABEL ANDROID IOS 6-stellige PIN now! + LABEL ANDROID IOS jetzt! @@ -2529,129 +4102,145 @@ klicken. This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand. + LABEL ANDROID IOS Funktioniert nur, wenn Sie Ihre 5-stellige Transport-PIN bereits in Ihre persönliche 6-stellige PIN geändert haben. + + App on iPhone <b>with</b> NFC chip as card reader + LABEL ANDROID IOS + App auf iPhone <b>mit</b> NFC-Chip als Kartenleser + + + ... and place the top of the iPhone onto the id card. + LABEL ANDROID IOS + ...und platzieren Sie die Oberkante des iPhones auf dem Ausweis. + TutorialReaderMethodSacDesktop Tutorial: Smartphone as card reader - Tutorial: Smartphone als Kartenlesegerät + LABEL ANDROID IOS + Tutorial: Smartphone als Kartenleser Smartphone as card reader - Smartphone als Kartenlesegerät + LABEL ANDROID IOS + Smartphone als Kartenleser App on computer <b>without</b> NFC chip + LABEL ANDROID IOS App auf Computer <b>ohne</b> NFC-Chip - - Android smartphone <b>with</b> NFC chip as card reader - Android-Telefon <b>mit</b> NFC-Chip als Kartenlesegerät - - - Install AusweisApp2 on both your computer <b>and</b> your android smartphone with NFC ability. - Installieren Sie AusweisApp2 auf Ihren Computer und Ihrem Android-Telefon mit NFC. - Both devices have to be connected to the same wifi network + LABEL ANDROID IOS Beide Geräte müssen im selben WLAN sein - - Now enter "Smartphone as card reader" in the app on your android smartphone... - Jetzt in der App auf Ihrem Android-Telefon im Menü auf „Smartphone als Kartenlesegerät“ tappen... - - - qrc:///images/tutorial/generated/reader_sac_menu_en.svg - qrc:///images/tutorial/generated/reader_sac_menu_de.svg - Start remote service + LABEL ANDROID IOS Fernzugriff starten Now + LABEL ANDROID IOS Jetzt Start pairing + LABEL ANDROID IOS Kopplung starten Next + LABEL ANDROID IOS Dann Pairing code + LABEL ANDROID IOS Kopplungscode appears! + LABEL ANDROID IOS erscheint! Start the App now on your computer and enter the settings. + LABEL ANDROID IOS Öffnen Sie nun die App auf dem Computer und gehen auf Einstellungen. Select the <b>Card Readers</b> tab. - Den Reiter <b>Kartenlesegeräte</b> auswählen. + LABEL ANDROID IOS + Den Reiter <b>Kartenleser</b> auswählen. qrc:///images/tutorial/screenshot_pairing_en.png + LABEL ANDROID IOS qrc:///images/tutorial/screenshot_pairing_de.png Select smartphone from list and click "pair" + LABEL ANDROID IOS Smartphone in der Liste wählen und auf „koppeln“ klicken Enter pairing code next. + LABEL ANDROID IOS Dann Kopplungscode eingeben. Click link on the website of the service provider. + LABEL ANDROID IOS Link auf Webseite des Diensteanbieters klicken. qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg + LABEL ANDROID IOS qrc:///images/tutorial/generated/reader_nfc_userdata_example_de.svg The App opens automatically. + LABEL ANDROID IOS Die App öffnet sich automatisch. The AusweisApp2 will display who wants to access which data. + LABEL ANDROID IOS Ihnen wird angezeigt, wer welche Ihrer Daten abfragen will. Start the process with a click on: + LABEL ANDROID IOS Starten Sie den Vorgang mit: - Identify now - Jetzt ausweisen + Proceed to PIN entry + LABEL ANDROID IOS + Weiter zur PIN-Eingabe qrc:///images/tutorial/generated/where_lay_down_id.svg + LABEL ANDROID IOS qrc:///images/tutorial/generated/where_lay_down_id.svg - - ... and place the id card flat onto the NFC interface. - ... und platzieren Sie den Ausweis flach an der NFC-Schnittstelle Ihres Android-Telefons. - Do not move device or id card! + LABEL ANDROID IOS Gerät und Ausweis nicht bewegen! The correct position is specific for your device... + LABEL ANDROID IOS Die genaue Position hängt von Ihrem Gerät ab... https://www.ausweisapp.bund.de/mobile-geraete/ + LABEL ANDROID IOS https://www.ausweisapp.bund.de/mobile-geraete/ @@ -2660,14 +4249,17 @@ klicken. Enter + LABEL ANDROID IOS Eingeben 6 digits long PIN + LABEL ANDROID IOS 6-stellige PIN now! + LABEL ANDROID IOS jetzt! @@ -2676,129 +4268,158 @@ klicken. This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand. + LABEL ANDROID IOS Funktioniert nur, wenn Sie Ihre 5-stellige Transport-PIN bereits in Ihre persönliche 6-stellige PIN geändert haben. + + qrc:///images/tutorial/generated/reader_sac_menu_%1_en.svg + LABEL ANDROID IOS + qrc:///images/tutorial/generated/reader_sac_menu_%1_de.svg + + + Smartphone <b>with</b> NFC chip as card reader + LABEL ANDROID IOS + Smartphone <b>mit</b> NFC-Chip als Kartenleser + + + Install AusweisApp2 on both your computer <b>and</b> your smartphone with NFC capability. + LABEL ANDROID IOS + Installieren Sie die AusweisApp2 <b>sowohl</b> auf Ihrem Rechner <b>als auch</b> auf Ihrem NFC-fähigen Smartphone. + + + Now choose "Remote" in the AusweisApp2 on your smartphone... + LABEL ANDROID IOS + Öffnen Sie nun in der AusweisApp2 auf dem Smartphone die Ansicht "Fernzugriff"... + + + Now choose "Smartphone as card reader" in the AusweisApp2 on your smartphone... + Öffnen Sie nun in der AusweisApp2 auf dem Smartphone den Bereich "Smartphone als Kartenleser"... + + + ... and place the id card onto the NFC interface. + LABEL ANDROID IOS + ...und platzieren Sie den Ausweis auf die NFC-Schnittstelle. + TutorialReaderMethodSacMobile Tutorial: Smartphone as card reader - Tutorial: Smartphone als Kartenlesegerät + LABEL ANDROID IOS + Tutorial: Smartphone als Kartenleser App on tablet or smartphone <b>without</b> NFC chip + LABEL ANDROID IOS App auf Tablet oder Telefon <b>ohne</b> NFC-Chip - - Android smartphone <b>with</b> NFC chip as card reader - Android-Telefon <b>mit</b> NFC-Chip als Kartenlesegerät - - - Install AusweisApp2 on both your device without NFC <b>and</b> your android smartphone with NFC ability. - Installieren Sie die AusweisApp2 auf Ihrem Gerät ohne NFC <b>und</b> auf Ihrem Android-Telefon mit NFC. - Both devices have to be connected to the same wifi network + LABEL ANDROID IOS Beide Geräte müssen im selben WLAN sein - - Now enter "Smartphone as card reader" in the app on your android smartphone <b>with</b>... - Jetzt in der App auf Ihrem Android-Telefon <b>mit</b> NFC im Menü auf „Smartphone als Kartenlesegerät“ tappen... - - - qrc:///images/tutorial/generated/reader_sac_menu_en.svg - qrc:///images/tutorial/generated/reader_sac_menu_en.svg - Start remote service + LABEL ANDROID IOS Fernzugriff starten Now + LABEL ANDROID IOS Jetzt Start pairing + LABEL ANDROID IOS Kopplung starten Next + LABEL ANDROID IOS Dann Pairing code + LABEL ANDROID IOS Kopplungscode appears! + LABEL ANDROID IOS erscheint! - Now open the app on your device <b>without</b> NFC and select <b>Smartphone as card reader</b>. - Nun die App auf dem Gerät <b>ohne</b> NFC öffnen und auf <b>Smartphone als Kartenlesegerät</b> gehen. + Now open the AusweisApp2 on your device <b>without</b> NFC and select <b>Smartphone as card reader</b>. + Öffnen Sie nun die AusweisApp2 auf dem Gerät <b>ohne</b> NFC und wählen <b>Smartphone als Kartenleser</b> aus. Now select <b>Settings</b>. + LABEL ANDROID IOS Dort <b>Einstellungen</b> auswählen. Choose smartphone from list + LABEL ANDROID IOS Smartphone aus Liste wählen Enter pairing code next. + LABEL ANDROID IOS Dann Kopplungscode eingeben. Click link on the website of the service provider on the device <b>without</b> NFC. + LABEL ANDROID IOS Auf dem Gerät <b>ohne</b> NFC Link auf Webseite des Diensteanbieters klicken. qrc:///images/tutorial/generated/reader_nfc_userdata_example_en.svg + LABEL ANDROID IOS qrc:///images/tutorial/generated/reader_nfc_userdata_example_de.svg The App opens automatically. + LABEL ANDROID IOS Die App öffnet sich automatisch. The AusweisApp2 will display who wants to access which data. + LABEL ANDROID IOS Ihnen wird angezeigt, wer welche Ihrer Daten abfragen will. Start the process with a click on: + LABEL ANDROID IOS Den Vorgang starten mit: - Identify now - Jetzt ausweisen - - - qrc:///images/tutorial/screenshot_choose_reader_en.png - qrc:///images/tutorial/screenshot_choose_reader_de.png + Proceed to PIN entry + LABEL ANDROID IOS + Weiter zur PIN-Eingabe Tap on Wifi + LABEL ANDROID IOS Tap auf WLAN qrc:///images/tutorial/generated/where_lay_down_id.svg + LABEL ANDROID IOS qrc:///images/tutorial/generated/where_lay_down_id.svg - - ... and place the id card flat onto the NFC interface. - ... und den Ausweis flach an der NFC-Schnittstelle Ihres Android-Telefons platzieren. - Do not move device or id card! + LABEL ANDROID IOS Gerät und Ausweis nicht bewegen! The correct position is specific for your device... + LABEL ANDROID IOS Die genaue Position hängt von Ihrem Gerät ab... https://www.ausweisapp.bund.de/mobile-geraete/ + LABEL ANDROID IOS https://www.ausweisapp.bund.de/mobile-geraete/ @@ -2807,14 +4428,17 @@ klicken. Enter + LABEL ANDROID IOS Eingeben 6 digits long PIN + LABEL ANDROID IOS 6-stellige PIN now! + LABEL ANDROID IOS jetzt! @@ -2823,29 +4447,74 @@ klicken. This is only possible if you have exchanged the 5 digits long transport PIN with a 6 digits long personal PIN beforehand. + LABEL ANDROID IOS Funktioniert nur, wenn Sie Ihre 5-stellige Transport-PIN bereits in Ihre persönliche 6-stellige PIN geändert haben. + + qrc:///images/tutorial/generated/reader_sac_menu_%1_en.svg + LABEL ANDROID IOS + qrc:///images/tutorial/generated/reader_sac_menu_%1_de.svg + + + Now open the AusweisApp2 on your device <b>without</b> NFC and select <b>Configure remote service</b>. + LABEL ANDROID IOS + Öffnen Sie nun die AusweisApp2 auf dem Gerät <b>ohne</b> NFC und wählen Sie <b>Fernzugriff konfigurieren</b> aus. + + + qrc:///images/tutorial/screenshot_choose_reader_%1_en.png + LABEL ANDROID IOS + qrc:///images/tutorial/screenshot_choose_reader_%1_de.png + + + Smartphone <b>with</b> NFC chip as card reader + LABEL ANDROID IOS + Smartphone <b>mit</b> NFC-Chip als Kartenleser + + + Install AusweisApp2 on both your device without NFC <b>and</b> your smartphone with NFC capability. + LABEL ANDROID IOS + Installieren Sie die AusweisApp2 auf Ihrem Gerät ohne NFC <b>und</b> auf Ihrem NFC-fähigen Smartphone. + + + Now choose "Remote" in the AusweisApp2 on your smartphone... + LABEL ANDROID IOS + Öffnen Sie nun in der AusweisApp2 auf dem Smartphone den Bereich "Fernzugriff"... + + + Now choose "Smartphone as card reader" in the AusweisApp2 on your smartphone... + Öffnen Sie nun in der AusweisApp2 auf dem Smartphone den Bereich "Smartphone als Kartenleser"... + + + ... and place the id card onto the NFC interface. + LABEL ANDROID IOS + ...und platzieren Sie den Ausweis auf die NFC-Schnittstelle. + TutorialView Tutorial + LABEL ANDROID IOS Tutorial What? + LABEL ANDROID IOS Was? Where? + LABEL ANDROID IOS Wo? How? + LABEL ANDROID IOS Wie? Important! + LABEL ANDROID IOS Wichtig! @@ -2853,101 +4522,120 @@ klicken. TutorialWhat What is the online ID function? + LABEL ANDROID IOS Was ist die Online-Ausweisfunktion? - - You can authenticate yourself in the internet - Mit ihr weisen Sie sich sicher im Internet aus - - - and use it to deal with administrative paperwork and business matters electronically! - und erledigen damit Behördengänge oder geschäftliche Angelegenheiten einfach elektronisch! - Alright, but is it secure? + LABEL ANDROID IOS Alles klar, nur ist das auch sicher? Of course, because we use a so called + LABEL ANDROID IOS Natürlich, dafür haben wir die sogenannte Mutual authentication + LABEL ANDROID IOS gegenseitige Authentifizierung ... it establishes a secure connection between ID document and service provider. + LABEL ANDROID IOS ...sie stellt eine sichere Verbindung zwischen Ausweisdokument und Diensteanbieter her. On every authentication you get displayed <b>who</b> wants to access <b>which</b> data + LABEL ANDROID IOS Ihnen wird immer zuerst angezeigt, <b>wer welche</b> Daten auslesen möchte and you consent to the request with your personal PIN. + LABEL ANDROID IOS und Sie stimmen der Abfrage mit Ihrer persönlichen PIN zu. ... is the provider authorized for this? + LABEL ANDROID IOS ... und darf der das? The provider needs an authorization of the Federal Office of Administration. + LABEL ANDROID IOS Der Anbieter benötigt eine Berechtigung vom Bundesverwaltungsamt. Certificate + LABEL ANDROID IOS Zertifikat Everytime both participants authenticate each other... + LABEL ANDROID IOS Es weisen sich also immer beide Seiten eindeutig aus... ... and therefore your data is protected and securely transfered. + LABEL ANDROID IOS ... so werden Ihre Daten geschützt und sicher übermittelt. - - qrc:///images/tutorial/screenshot_cert_en.png - qrc:///images/tutorial/screenshot_cert_de.png - https://www.youtube.com/watch?v=fzbUZmHaZp4&index=5&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm + LABEL ANDROID IOS https://www.youtube.com/watch?v=fzbUZmHaZp4&index=5&list=PLLB5ERhVkn25qQXgMHQr-1KgyZsJKoSAm You can also watch a video on YouTube on this topic + LABEL ANDROID IOS Sie können sich hierzu auch ein Video auf YouTube anschauen + + You can use it to authenticate yourself in the internet + LABEL ANDROID IOS + Mit ihr weisen Sie sich sicher im Internet aus + + + and also to deal with administrative paperwork and business matters electronically! + LABEL ANDROID IOS + und erledigen damit Behördengänge oder geschäftliche Angelegenheiten einfach elektronisch! + + + qrc:///images/tutorial/screenshot_cert_%1_en.png + LABEL ANDROID IOS + qrc:///images/tutorial/screenshot_cert_%1_de.png + TutorialWhere On every website of a service provider where you see this icon: + LABEL ANDROID IOS Überall, wo Sie auf der Webseite eines Online-Dienstes folgendes Logo sehen: By the way, you can find many services directly in the AusweisApp2 <b>provider list</b>. + LABEL ANDROID IOS Übrigens, viele Dienste finden Sie direkt in der <b>Anbieterliste</b> der AusweisApp2. The <b>integrated self-disclosure</b> is a special service to view the data saved on your ID card. + LABEL ANDROID IOS Die <b>integrierte Selbstauskunft</b> ist ein spezieller Dienst, um Daten einzusehen, die auf Ihrem Ausweis gespeichert sind. - - You can access it by clicking "See my personal data" on the AusweisApp2 start page, followed by "Identify now" - Sie erreichen sie von der Startseite der AusweisApp2 mit "Meine Daten einsehen" und dann auf "Jetzt ausweisen" - And this is how it works + LABEL ANDROID IOS Und so funkioniert's The AusweisApp2 will always display <b>who</b> wants to access <b>which</b> of your data. + LABEL ANDROID IOS Die AusweisApp2 zeigt Ihnen immer an, wer welche Ihrer Daten abfragen will. qrc:///images/tutorial/generated/where_userdata_example_en.svg + LABEL ANDROID IOS qrc:///images/tutorial/generated/where_userdata_example_de.svg @@ -2960,6 +4648,7 @@ klicken. Enter + LABEL ANDROID IOS Eingeben @@ -2968,10 +4657,12 @@ klicken. 6 digits long PIN + LABEL ANDROID IOS 6-stellige PIN now! + LABEL ANDROID IOS jetzt! @@ -2980,15 +4671,38 @@ klicken. Where can I use the online ID function? + LABEL ANDROID IOS Wo kann ich die Online-Ausweisfunktion nutzen? - qrc:///images/tutorial/generated/where_providerlist_screenshot_en.svg - qrc:///images/tutorial/generated/where_providerlist_screenshot_de.svg + qrc:///images/tutorial/generated/where_identify_now_en.svg + LABEL ANDROID IOS + qrc:///images/tutorial/generated/where_identify_now_de.svg - qrc:///images/tutorial/generated/where_identify_now_en.svg - qrc:///images/tutorial/generated/where_identify_now_de.svg + qrc:///images/tutorial/generated/where_providerlist_screenshot_android_en.svg + LABEL ANDROID + qrc:///images/tutorial/generated/where_providerlist_screenshot_android_de.svg + + + qrc:///images/tutorial/screenshot_providerlist_ios_en.png + LABEL IOS + qrc:///images/tutorial/screenshot_providerlist_ios_de.png + + + You can access the self-disclosure by clicking "See my personal data" on the AusweisApp2 start page, followed by "Proceed to PIN entry" + LABEL ANDROID IOS + Sie erreichen die Selbstauskunft indem Sie auf der Startseite der AusweisApp2 "Meine Daten einsehen" und dann "Weiter zur PIN-Eingabe" auswählen + + + Now lay down your ID card and hold the top of your iPhone to the ID card. + LABEL ANDROID IOS + Legen Sie nun Ihren Ausweis vor sich hin und halten Sie die Oberkante des iPhones an den Ausweis. + + + Don't move your iPhone during the procedure! + LABEL ANDROID IOS + iPhone während des gesamten Vorgangs nicht bewegen! @@ -3034,83 +4748,65 @@ klicken. VersionInformation Version Information + LABEL ANDROID IOS Versionsinformationen + + Version information + LABEL DESKTOP_QML + Versionsinformationen + + + This is the version information section of the AusweisApp2. + Dies ist die Anzeige der Versionsinformationen der AusweisApp2.. + WhiteListSurveyView Feedback + LABEL ANDROID IOS Feedback Would you like to help us to improve the AusweisApp2? + INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Part of content text Möchten Sie uns helfen, die AusweisApp2 zu verbessern? Supplying your device characteristics helps us to gather reliable information about the compatibility of your device. + INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Part of content text Mit der Übermittlung Ihrer Gerätedaten helfen Sie uns, verlässliche Aussagen über die Kompatibilität Ihres Gerätes mit der AusweisApp2 zu treffen. The transmission is anonymous. No personal data is collected or transmitted! + INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Part of content text Die Übermittlung erfolgt anonym und es werden keine personenbezogenen Daten übermittelt! - - The following information will be transmitted, if you decide so: - Nach Ihrer Bestätigung werden folgende Daten übermittelt: - - - Vendor - Name des Herstellers - - - Model number - Modellnummer - - - Model name - Modellname - - - Collection date - Datum der Erfassung - - - AusweisApp2 version - Version der AusweisApp2 - - - ROM build number - Build-Nummer des Betriebssystems - - - Android version - Android-Version - - - Kernel version - Kernel-Version - - - Max. NFC packet length - Max. NFC-Paket-Länge - Thank you for your assistance! + INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Thank you message Vielen Dank für Ihre Mithilfe! Cancel + LABEL ANDROID IOS Abbrechen Transmit + LABEL ANDROID IOS Senden Send device data? + INFO ANDROID IOS Request to the user if the device information should be shared for statistics (Whitelist) - Header Gerätedaten senden? + + Collected data + Identifizierte Eigenschaften + governikus::AboutDialog @@ -3139,134 +4835,167 @@ klicken. governikus::AccessRoleAndRightsUtil WRITE_DG17 + LABEL ALL_PLATFORMS WRITE_DG17 WRITE_DG18 + LABEL ALL_PLATFORMS WRITE_DG18 WRITE_DG19 + LABEL ALL_PLATFORMS WRITE_DG19 WRITE_DG20 + LABEL ALL_PLATFORMS WRITE_DG20 WRITE_DG21 + LABEL ALL_PLATFORMS WRITE_DG21 Optional data + LABEL ALL_PLATFORMS Optionale Daten Residence permit II + LABEL ALL_PLATFORMS Nebenbestimmungen II Residence permit I + LABEL ALL_PLATFORMS Nebenbestimmungen I (nur eAT) Pseudonym + LABEL ALL_PLATFORMS Pseudonym Address + LABEL ALL_PLATFORMS Anschrift Birth name + LABEL ALL_PLATFORMS Geburtsname Date of birth + LABEL ALL_PLATFORMS Geburtsdatum Doctoral degree + LABEL ALL_PLATFORMS Doktorgrad Religious / artistic name + LABEL ALL_PLATFORMS Ordens- / Künstlername Given name(s) + LABEL ALL_PLATFORMS Vorname(n) Valid until + LABEL ALL_PLATFORMS Gültig bis Issuing country + LABEL ALL_PLATFORMS Ausstellender Staat Address verification + LABEL ALL_PLATFORMS Wohnortbestätigung RFU + LABEL ALL_PLATFORMS RFU Community-ID + LABEL ALL_PLATFORMS Wohnort-ID Gender + LABEL ALL_PLATFORMS Geschlecht Nationality + LABEL ALL_PLATFORMS Staatsangehörigkeit Place of birth + LABEL ALL_PLATFORMS Geburtsort Family name + LABEL ALL_PLATFORMS Familienname Document type + LABEL ALL_PLATFORMS Dokumentenart Installation of qualified signature certificates + LABEL ALL_PLATFORMS Installation qualifizierter Signaturzertifikate Installation of signature certificates + LABEL ALL_PLATFORMS Installation von Signaturzertifikaten PIN Management + LABEL ALL_PLATFORMS PIN-Verwaltung CAN allowed + LABEL ALL_PLATFORMS CAN erlaubt Privileged terminal + LABEL ALL_PLATFORMS Privilegiertes Terminal Age verification + LABEL ALL_PLATFORMS Altersbestätigung Unknown (reserved) + LABEL ALL_PLATFORMS Unbekannt (reserviert) Unknown + LABEL ALL_PLATFORMS Unbekannt @@ -3328,6 +5057,14 @@ klicken. Did you change the transport PIN already?<br><br>Prior to the first use of the online identification function you have to replace the transport PIN by an individual 6-digit PIN. Online identification with transport PIN is not possible. Haben Sie Ihre Transport-PIN schon geändert? <br><br>Bevor Sie das erste Mal die Online-Ausweisfunktion nutzen können, müssen Sie Ihre Transport-PIN durch eine persönliche, 6-stellige PIN ersetzen. Die Benutzung der Online-Ausweisfunktion ist mit der Transport-PIN nicht möglich. + + Switch UI + Grafische Oberfläche wechseln + + + Do you want to switch to the new beta UI? You can switch back to the old UI in "Settings". + Möchten Sie die neue grafische Oberfläche ausprobieren? Sie können in den Einstellungen zur alten Oberfläche zurückwechseln. + governikus::AppQtMainWidget @@ -3349,46 +5086,61 @@ klicken. not inserted Karte + ERROR ALL_PLATFORMS No card is present/inserted. The text is only used in DiagnosisView. nicht eingelegt unknown type Karte + ERROR ALL_PLATFORMS An unknown card is present/inserted. The text is only used in DiagnosisView. unbekannter Typ ID card (PA/eAT) + ERROR ALL_PLATFORMS An id card is present/inserted. The text is only used in DiagnosisView. Ausweisdokument (PA/eAT) + + Passport + ERROR ALL_PLATFORMS A passport card is present/inserted. The text is only used in DiagnosisView. + Reisepass + governikus::CertificateDescriptionModel Service provider + LABEL ALL_PLATFORMS Diensteanbieter Certificate issuer + LABEL ALL_PLATFORMS Aussteller des Berechtigungszertifikats Name, address and mail address of the service provider + LABEL ALL_PLATFORMS Name, Adresse und E-Mail vom Diensteanbieter Purpose + LABEL ALL_PLATFORMS Zweck Indication of the bodies responsible for the service provider, that verify the compliance with data security regulations - Angabe der für den Dienstanbieter zuständigen Datenschutzaufsicht + LABEL ALL_PLATFORMS + Angabe der für den Diensteanbieter zuständigen Datenschutzaufsicht Service provider information - Dienstanbieterinformationen + LABEL ALL_PLATFORMS + Diensteanbieterinformationen Validity + LABEL ALL_PLATFORMS Gültigkeit @@ -3484,21 +5236,9 @@ klicken. governikus::DiagnosisModel - - Operating system - Betriebssystem - Card reader - Kartenlesegerät - - - PC/SC - PC/SC - - - Firewall - Firewall + Kartenleser Time of diagnosis @@ -3526,7 +5266,7 @@ klicken. Basic card reader - Basis-Kartenlesegerät + Basis-Kartenleser Standard / comfort card reader @@ -3542,7 +5282,7 @@ klicken. Retry counter: %1 - Fehlbedienungszähler: %1 + Verbleibende Versuche: %1 Components @@ -3560,10 +5300,6 @@ klicken. d. MMMM yyyy, hh:mm:ss AP d. MMMM yyyy, HH:mm:ss 'Uhr' - - Diagnosis data - Diagnosedaten - Hardware address: %1 Hardwareadresse: %1 @@ -3572,22 +5308,10 @@ klicken. No Antivirus information available on this platform. Keine Antivirus-Informationen auf dieser Platform verfügbar. - - No devices paired - Keine gekoppelten Geräte - dd.MM.yyyy, hh:mm:ss dd.MM.yyyy, hh:mm:ss - - No information found for this certificate - Keine Informationen für dieses Zertifikat verfügbar - - - Proxy - Proxy - Hostname: %1 Hostname: %1 @@ -3616,10 +5340,6 @@ klicken. Connection test with proxy: Failed Proxy-Verbindungstest: Fehlgeschlagen - - No Proxy Found - Kein Proxy gefunden - Connection test without proxy: Successful Verbindungstest ohne Proxy: Erfolgreich @@ -3681,8 +5401,127 @@ klicken. Eingehende AusweisApp2-Regel - Windows Firewall profiles - Windows-Firewall-Profile + Certificate fingerprint: %1 + Zertifikats-Fingerabdruck: %1 + + + Last connection: %1 + Zuletzt verbunden: %1 + + + <Not set> + <Nicht gesetzt> + + + No IP addresses assigned + Keine IP-Adressen zugewiesen + + + Warning: The current firewall status can be obscured by additional Group Policies on your system, often set by system administrators. + Warnung: Der aktuelle Firewallstatus kann durch zusätzliche Gruppenregeln, die oftmals von Administratoren gesetzt werden, auf Ihrem System verschleiert sein. + + + An error occurred while trying to gather firewall information. Please check the log for more information. + Beim Sammeln von Firewallinformationen ist ein Fehler aufgetreten. Bitte konsultieren Sie das Protokoll für weitere Informationen. + + + No Antivirus software detected. + Es konnte keine Antivirus-Software erkannt werden. + + + Third party firewalls cannot be detected on Windows 7. + Firewalls von Drittherstellern können auf Windows 7 nicht erkannt werden. + + + PC/SC information + PC/SC Informationen + + + Paired remote devices + Gekoppelte Netzwerkgeräte + + + Antivirus information + Antivirusinformationen + + + Firewall information + Firewallinformationen + + + Failed to retrieve date & time + Datum & Uhrzeit konnten nicht ermittelt werden + + + IPv4 address: %1 + IPv4-Adresse: %1 + + + IPv6 address: %1 + IPv6-Adresse: %1 + + + Unknown address: %1 + Unbekannte Adresse: %1 + + + Interface: "%1" + Schnittstelle: %1 + + + No proxy found + Kein Proxy erkannt + + + Proxy information + Proxyinformationen + + + Windows firewall profiles + Windows Firewall-Profile + + + No devices paired. + Keine Geräte gekoppelt. + + + No information found for this certificate. + Für dieses Zertifikat konnten keine Informationen ermittelt werden. + + + Connected Card reader + Verbundene Kartenleser + + + No supported reader found. + Keine unterstützten Leser erkannt. + + + Network + Netzwerk + + + Antivirus and firewall + Antivirus und Firewall + + + + governikus::DiagnosisTreeModel + + Diagnosis data + Diagnosedaten + + + Operating system + Betriebssystem + + + Card reader + Kartenleser + + + PC/SC + PC/SC Paired devices @@ -3700,22 +5539,106 @@ klicken. Installed antivirus software Installierte Antivirus-Software + + Firewall + Firewall + + + Time of diagnosis + Diagnosezeitpunkt + + + Diagnosis is running... + Diagnose läuft... + + + No Antivirus information available on this platform. + Keine Antivirus-Informationen auf dieser Platform verfügbar. + + + No Firewall information available on this platform. + Auf dieser Platform sind keine Firewallinformationen verfügbar. + + + Vendor: %1 + Hersteller: %1 + + + Version: %1 + Version: %1 + + + File path: %1 + Dateipfad: %1 + Not recognised Nicht erkannt + + Basic card reader + Basis-Kartenleser + + + Standard / comfort card reader + Standard- / Komfortleser + + + Type: %1 + Typ: %1 + + + Card: %1 + Karte: %1 + + + Retry counter: %1 + Verbleibende Versuche: %1 + + + Components + Komponenten + + + Driver + Treiber + + + No devices paired + Keine gekoppelten Geräte + Certificate fingerprint: %1 Zertifikats-Fingerabdruck: %1 + + dd.MM.yyyy, hh:mm:ss + dd.MM.yyyy, hh:mm:ss + Last connection: %1 Zuletzt verbunden: %1 + + No information found for this certificate + Keine Informationen für dieses Zertifikat verfügbar + + + Initial diagnosis running, please wait. + Führe initiale Diagnose durch, bitte warten. + + + d. MMMM yyyy, hh:mm:ss AP + d. MMMM yyyy, HH:mm:ss 'Uhr' + <Not set> <Nicht gesetzt> + + Hardware address: %1 + Hardwareadresse: %1 + No IP addresses assigned Keine IP-Adressen zugewiesen @@ -3724,6 +5647,114 @@ klicken. IP addresses IP-Adressen + + Proxy + Proxy + + + Hostname: %1 + Hostname: %1 + + + Port: %1 + Port: %1 + + + Capabilities: %1 + Fähigkeiten: %1 + + + Ping test to proxy: Successful + Proxy-Pingtest: Erfolgreich + + + Ping test to proxy: Failed + Proxy-Pingtest: Fehlgeschlagen + + + Connection test with proxy: Successful + Proxy-Verbindungstest: Erfolgreich + + + Connection test with proxy: Failed + Proxy-Verbindungstest: Fehlgeschlagen + + + No Proxy Found + Kein Proxy gefunden + + + Connection test without proxy: Successful + Verbindungstest ohne Proxy: Erfolgreich + + + Connection test without proxy: Failed + Verbindungstest ohne Proxy: Fehlgeschlagen + + + No Antivirus software detected. + Es konnte keine Antivirus-Software erkannt werden. + + + Last updated: %1 + Zuletzt aktualisiert: %1 + + + Executable path: %1 + Anwendungspfad: %1 + + + Antivirus detection failed. + Antivirus-Erkennung fehlgeschlagen. + + + Yes + Ja + + + No + Nein + + + Third party firewalls cannot be detected on Windows 7. + Firewalls von Drittherstellern können auf Windows 7 nicht erkannt werden. + + + No third party firewalls detected + Keine Drittanbieterfirewall erkannt + + + Firewalls from third party vendors + Firewalls von Drittanbietern + + + Enabled: %1 + Aktiviert: %1 + + + Up to date: %1 + Auf dem aktuellen Stand: %1 + + + Windows firewall rules + Windows-Firewallregeln + + + Outgoing AusweisApp2 rule + Ausgehende AusweisApp2-Regel + + + Exists: %1 + Existiert: %1 + + + Incoming AusweisApp2 rule + Eingehende AusweisApp2-Regel + + + Windows Firewall profiles + Windows-Firewall-Profile + Warning: The current firewall status can be obscured by additional Group Policies on your system, often set by system administrators. Warnung: Der aktuelle Firewallstatus kann durch zusätzliche Gruppenregeln, die oftmals von Administratoren gesetzt werden, auf Ihrem System verschleiert sein. @@ -3732,115 +5763,132 @@ klicken. An error occurred while trying to gather firewall information. Please check the log for more information. Beim Sammeln von Firewallinformationen ist ein Fehler aufgetreten. Bitte konsultieren Sie das Protokoll für weitere Informationen. - - No Antivirus software detected. - Es konnte keine Antivirus-Software erkannt werden. - - - Third party firewalls cannot be detected on Windows 7. - Firewalls von Drittherstellern können auf Windows 7 nicht erkannt werden. - governikus::ECardApiResult An unexpected error has occurred during processing. + LABEL ALL_PLATFORMS Während der Verarbeitung ist ein unerwarteter Fehler aufgetreten. Use of the function by the client application is not permitted. + LABEL ALL_PLATFORMS Für die Operation fehlen die benötigten Rechte. An internal error has occurred during processing. + LABEL ALL_PLATFORMS Während der Verarbeitung ist ein interner Fehler aufgetreten. There was some problem with a provided or omitted parameter. + LABEL ALL_PLATFORMS Ein fehlerhafter Parameter wurde übermittelt. The API function is unknown. + LABEL ALL_PLATFORMS Die Schnittstellenfunktion ist unbekannt. The framework or layer has not been initialized. + LABEL ALL_PLATFORMS Die Schnittstelle ist nicht initialisiert. The active session has been terminated. + LABEL ALL_PLATFORMS Die aktuelle Sitzung wurde beendet. A Communication error occurred during processing. + LABEL ALL_PLATFORMS Während der Verarbeitung ist ein Verbindungsfehler aufgetreten. The operation was terminated as the set time was exceeded. + LABEL ALL_PLATFORMS Der Vorgang wurde wegen einer Zeitüberschreitung abgebrochen. The operation was aborted as an invalid channel handle was used. + LABEL ALL_PLATFORMS Der Vorgang wurde wegen einer ungültigen Verbindung abgebrochen. A trusted channel could not be opened. + LABEL ALL_PLATFORMS Es konnte keine sichere Verbindung hergestellt werden. The operation was aborted as an unknown protocol was used. + LABEL ALL_PLATFORMS Der Vorgang wurde wegen der Verwendung eines unbekannten Protokolls abgebrochen. The operation was aborted as an unknown cipher suite was used. + LABEL ALL_PLATFORMS Der Vorgang wurde wegen der Verwendung eines unbekannten Verschlüsselungsalgorithmus abgebrochen. The operation was aborted as an unknown web service binding was used. + LABEL ALL_PLATFORMS Der Vorgang wurde wegen der Verwendung eines unbekannten Webservice-Bindings abgebrochen. The card is missing or was removed. + LABEL ALL_PLATFORMS Es liegt keine Karte auf oder sie wurde entfernt. The new PIN and the confirmation do not match. + LABEL ALL_PLATFORMS Die neue PIN und ihre Wiederholung stimmen nicht überein. The format of the PIN is wrong. + LABEL ALL_PLATFORMS Die Zeichenanzahl der PIN ist inkorrekt. Signature certificate key generation is not possible. + LABEL ALL_PLATFORMS Die Erzeugung eines Schlüssels für das Signaturzertifikat war nicht möglich. The process was cancelled by the user. + LABEL ALL_PLATFORMS Der Benutzer hat den Vorgang abgebrochen. One or more certificate checks failed. The operation will be aborted due to security reasons. + LABEL ALL_PLATFORMS Eine oder mehrere Zertifikatsprüfungen schlugen fehl. Der Vorgang wird aus Sicherheitsgründen abgebrochen. This action cannot be performed. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. + LABEL ALL_PLATFORMS Diese Aktion kann leider nicht durchgeführt werden. Die Online-Ausweisfunktion Ihres Ausweisdokuments ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. The authenticity of your ID card could not be verified. Please make sure that you are using a genuine ID card. Please note that test applications require the use of a test ID card. + LABEL ALL_PLATFORMS Die Echtheit Ihres Ausweisdokuments konnte nicht überprüft werden. Bitte stellen Sie sicher, dass Sie ein echtes Ausweisdokument verwenden. Bitte beachten Sie, dass Sie bei Testanwendungen einen Testausweis verwenden müssen. The age verification failed. + LABEL ALL_PLATFORMS Die Altersverifikation war nicht erfolgreich. The community verification failed. + LABEL ALL_PLATFORMS Die Wohnortverifikation war nicht erfolgreich. The ID card is invalid or disabled. + LABEL ALL_PLATFORMS Das Ausweisdokument ist ungültig oder gesperrt. @@ -3848,275 +5896,317 @@ klicken. governikus::GlobalStatus No error occurred. + ERROR ALL_PLATFORMS No actual error occured, required to provide a message for status code No_Error. Es ist kein Fehler aufgetreten. An unexpected error has occurred during processing. + ERROR ALL_PLATFORMS An unknown error from any of the subcomponents (PCSC, EcardAPI, QtNetwork, ...) occurred. Während der Verarbeitung ist ein unerwarteter Fehler aufgetreten. - The ID card has been removed. The process is aborted. - Das Ausweisdokument wurde entfernt. Der Vorgang wird abgebrochen. + The connection to the ID card has been lost. The process was aborted. + ERROR ALL_PLATFORMS The card was removed after the PACE channel was established. + Die Verbindung zum Ausweisdokument wurde unterbrochen. Der Vorgang wird abgebrochen. The authenticity of your ID card could not be confirmed. + ERROR ALL_PLATFORMS The certificates supplied by the card did not pass the authenticity check, further operation is not allowed. Die Echtheit Ihres Ausweisdokuments konnte nicht bestätigt werden. The program received an unknown message from the server. + ERROR_MASKED ALL_PLATFORMS The type of a POAS message could not be determined. Die Anwendung hat eine unbekannte Nachricht vom Server erhalten. The program received an unexpected message from the server. + ERROR_MASKED ALL_PLATFORMS The server sent a valid PAOS message but its type was unexpected. Die Anwendung hat eine nicht erwartete Nachricht vom Server erhalten. Pre-verification failed. + ERROR_MASKED ALL_PLATFORMS The certificates submitted by the server failed the authenticity check during pre-verification. Eine oder mehrere Zertifikatsprüfungen schlugen fehl. No unique AT CVC + ERROR_MASKED ALL_PLATFORMS The EAC1 request contained more than one At certificate. Kein eindeutiges AT CVC No unique DV CVC + ERROR_MASKED ALL_PLATFORMS The EAC1 request contained more than one Dv certificate. Kein eindeutiges DV CVC Authentication failed. + ERROR ALL_PLATFORMS DidAuthenticateEAC2, AA2 or the id card declined the certificates. Die Authentisierung ist fehlgeschlagen. No certificate description available. + ERROR_MASKED ALL_PLATFORMS Keine Zertifikatsbeschreibung vorhanden. No subject url available in certificate description. + ERROR_MASKED ALL_PLATFORMS Es konnte keine URL in der Zertifikatsbeschreibung gefunden werden. The certificate description does not match the certificate. + ERROR_MASKED ALL_PLATFORMS Die Zertifikatsbeschreibung passt nicht zum Zertifikat. The subject URL in the certificate description and the TCToken URL don't satisfy the same origin policy. + ERROR_MASKED ALL_PLATFORMS Die URL in der Zertifikatsbeschreibung und die TCToken-URL erfüllen die Same-Origin-Policy nicht. - - Failed to establish secure connection - Die sichere Verbindung konnte nicht aufgebaut werden - The program received an error from the server. + ERROR_MASKED ALL_PLATFORMS Die Anwendung hat einen Fehler vom Server erhalten. Received no data. + ERROR_MASKED ALL_PLATFORMS Received an empty TC token. Keine Daten erhalten. Cannot start authentication. An operation is already in progress. + ERROR ALL_PLATFORMS An ActivationHandler is requested even though an operation is currently still running. Die Authentisierung kann nicht gestartet werden. Es läuft bereits eine Operation. After three wrong entries your PIN is blocked. Please use the PIN management in this app to unblock it with the help of your PUK. + ERROR ALL_PLATFORMS The id card was blocked after three wrongfully entered PINs, the PUK is required to unlock the card. Hint to do so in the PIN management section of the app. Ihre PIN ist nach dreimaliger Fehleingabe gesperrt. Verwenden Sie die PIN-Verwaltung dieser App, um diese mit Ihrer PUK zu entsperren. Using the developer mode is only allowed in a test environment. + ERROR ALL_PLATFORMS The developer mode is enabled but a productive environment was detected. Der Entwicklermodus darf nur in einer Testumgebung verwendet werden. The service is temporarily not available. Please try again later. + ERROR ALL_PLATFORMS A server has responded with an HTTP error code 503. Der Dienst ist vorübergehend nicht verfügbar, bitte versuchen Sie es zu einem späteren Zeitpunkt erneut. Establishing a connection is taking too long. + ERROR_MASKED ALL_PLATFORMS The TCP connection to the server timed out. Der Verbindungsaufbau dauert zu lange. Establishing a connection via the proxy did not succeed. + ERROR_MASKED ALL_PLATFORMS The connection using a proxy failed, it may be misconfigured.. Der Verbindungsaufbau über den Proxy war nicht erfolgreich. It wasn't possible to connect to the server: the server sent a non-standard response. + ERROR_MASKED ALL_PLATFORMS An critical error while retrieving or parsing the TC token occurred. Die Verbindung zum Server ist fehlgeschlagen: Der Server hat eine nicht-standard Antwort gesendet. It wasn't possible to connect to the server: a secure connection could not be established. + ERROR_MASKED ALL_PLATFORMS Critical error from Qt's TLS API. Die Verbindung zum Server ist fehlgeschlagen: Es konnte keine sichere Verbindung aufgebaut werden. Application was invoked with wrong parameters. + ERROR_MASKED ALL_PLATFORMS The TC token URL could not be parsed/validated. Die Anwendung wurde mit den falschen Parametern aufgerufen. An unknown network error occurred. + ERROR_MASKED ALL_PLATFORMS Other critial network error by Qt. Ein unbekannter Netzwerkfehler ist aufgetreten. The selected card reader cannot be accessed anymore. - Das ausgewählte Kartenlesegerät kann nicht mehr angesprochen werden. - - - An error occurred while communicating with the card reader. - Bei der Kommunikation mit dem Kartenlesegerät ist ein Fehler aufgetreten. + ERROR ALL_PLATFORMS The card reader was removed after the PACE channel was established. + Der ausgewählte Kartenleser kann nicht mehr angesprochen werden. The server provided no or incomplete information. Your personal data could not be read out. + ERROR ALL_PLATFORMS The result of the self authentication could not be retrieved from the eService. Der Server lieferte keine oder nur unvollständige Informationen. Ihre persönlichen Daten konnten nicht ausgelesen werden. Error while connecting to the service provider. The SSL connection uses an unsupported key algorithm or length. + ERROR_MASKED ALL_PLATFORMS Fehler bei der Verbindung mit dem Diensteanbieter. Der Verschlüsselungsalgorithmus oder die Länge des Schlüssels der SSL-Verbindung wird nicht unterstützt. Empty redirect URL + ERROR_MASKED ALL_PLATFORMS The redirect URL could not be determined because the server sent an empty response. Leere Redirect-URL Expected redirect, got %1 + ERROR_MASKED ALL_PLATFORMS The redirect URL could not be determined due to an erroneous HTTP code. Erwartete HTTP-redirect, tatsächlich erhalten: %1 Invalid scheme: %1 + ERROR_MASKED ALL_PLATFORMS The redirect URL could not be determined because the redirect URL did not adhere to the HTTPS scheme. Ungültiges URL-Schema: %1 Malformed redirect URL: %1 + ERROR_MASKED ALL_PLATFORMS The redirect URL could not be determined because the redirect URL was invalid. Nicht wohlgeformte Redirect-URL: %1 The process was cancelled by the user. + ERROR ALL_PLATFORMS The user cancelled the authentication in either the UI or the card reader. Der Benutzer hat den Vorgang abgebrochen. The maximum time was exceeded during input process. + ERROR ALL_PLATFORMS The card reader signalled that it timed out. Bei der Eingabe wurde die maximale Zeit überschritten. Card does not exist + ERROR ALL_PLATFORMS Internal error, either PCSC, SaK or card reader could not find the id card. Karte nicht vorhanden An error occurred while communicating with the ID card. Please make sure that your ID card is placed correctly on the card reader and try again. + ERROR ALL_PLATFORMS Communication with the card failed due to the specification of the TR (Technische Richtlinie), Bei der Kommunikation mit dem Ausweisdokument ist ein Fehler aufgetreten. Bitte überprüfen Sie, dass das Ausweisdokument korrekt aufgelegt ist und versuchen Sie es erneut. A protocol error occurred. Please make sure that your ID card is placed correctly on the card reader and try again. If the problem occurs again, please contact our support at + ERROR ALL_PLATFORMS Communication with the card failed due to the specification of the TR (Technische Richtlinie). The protocol was faulty or invalid values were requested/received, Ein Protokollfehler ist aufgetreten. Bitte überprüfen Sie, dass das Ausweisdokument korrekt aufgelegt ist und versuchen Sie es erneut. Wenn das Problem wieder auftritt kontaktieren Sie bitte unseren Support unter - - https://www.ausweisapp.bund.de/en/qa/support/ - https://www.ausweisapp.bund.de/fragen-und-antworten/support/ - AusweisApp2 Support AusweisApp2 Support The given PIN is invalid. + ERROR ALL_PLATFORMS The id card declined the PIN. Die eingegebene PIN ist ungültig. The given card access number (CAN) is invalid. + ERROR ALL_PLATFORMS The id card declined the CAN. Die eingegebene Zugangsnummer (CAN) ist ungültig. The given PUK is invalid. + ERROR ALL_PLATFORMS The id card declined the PUK. Die eingegebene PUK ist ungültig. The PIN was blocked after too many unsuccessful attempts. + ERROR ALL_PLATFORMS The id card refused the PIN since the PIN feature is blocked after too many wrong attemps. Die PIN ist nach zu vielen Fehlversuchen gesperrt. The PIN is not blocked. + ERROR ALL_PLATFORMS It was attempted to unlock the id card via PUK even though it was not locked in the first place. This scenario is avoided in the UI by hiding the respective UI elements. Die PIN ist nicht gesperrt. The PUK was used ten times and is set inoperative. Please contact the competent authority that issued your ID document to unlock the PIN. + ERROR ALL_PLATFORMS The card declined the PUK since it was entered wrongfully 10 times, the local authorities have to be contacted to unlock the id card. Die eingegebene PUK wurde zehn Mal verwendet und ist außer Betrieb. Bitte kontaktieren Sie die für die Ausgabe Ihres Ausweisdokuments zuständige Behörde um Ihre PIN zu entsperren. The new PIN and the confirmation do not match. + ERROR ALL_PLATFORMS The card reader signalled that the new PIN was not confirmed correctly. Die neue PIN und ihre Wiederholung stimmen nicht überein. The length of the new PIN is not valid. + ERROR ALL_PLATFORMS The card reader declined the new PIN since its length was invalid. Die neue PIN hat eine ungültige Länge. An error occurred while connecting to a reader device. - Es ist ein Fehler während des Verbindungsaufbaus zu einem Kartenlesegerät aufgetreten. + ERROR ALL_PLATFORMS Error while connecting to a bluetooth card reader. + Es ist ein Fehler während des Verbindungsaufbaus zu einem Kartenleser aufgetreten. An error occurred while scanning for reader devices. - Es ist ein Fehler während der Suche nach einem Kartenlesegerät aufgetreten. - - - The remote reader connection was closed properly. - Die Verbindung zum entfernten Kartenlesegerät wurde ordnungsgemäß geschlossen. + ERROR ALL_PLATFORMS Error while searching for bluetooth card reader. + Es ist ein Fehler während der Suche nach einem Kartenleser aufgetreten. The remote card reader connection was not closed properly. + ERROR ALL_PLATFORMS The connection to the smartphone card reader (SaK) was lost. Die Verbindung zum Remote-Lesegerät wurde abgebrochen. - - Undefined error code occured when the remote card reader connection was closed. - Unbekannter Fehler beim Beenden der Verbindung zum Remote-Lesegerät. - Remote reader connection request contains invalid parameters. - Die Verbindungsanforderung zum entfernten Kartenlesegerät enthält einen ungültigen Parameter. - - - Empty password in extended encryption of remote reader connection request. - Die Anforderung der erweiterten Verschlüsselung mit dem entfernten Kartenlesegerät enthält ein leeres Kennwort. + ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) was invalid (missing device ID). + Die Verbindungsanforderung zum entfernten Kartenleser enthält einen ungültigen Parameter. Your remote reader version is incompatible with the local version. Please install the latest AusweisApp2 version on both your smartphone and your computer. - Die Version Ihres Smartphones als Kartenlesegerätes ist inkompatibel. Bitte aktualisieren Sie Ihre Installation. + ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) was invalid (API mismatch). + Die Version Ihres Smartphones als Kartenleser ist inkompatibel. Bitte aktualisieren Sie Ihre Installation. A timeout occurred while trying to establish a connection to a remote reader. - Bei der Verbindung zum entfernten Kartenlesegerät kam es zu einer Zeitüberschreitung. + ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) timed out. + Bei der Verbindung zum entfernten Kartenleser kam es zu einer Zeitüberschreitung. An error occurred while trying to establish a connection to a remote reader. - Bei der Verbindung zum entfernten Kartenlesegerät ist ein Fehler aufgetreten. + ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) failed due to network errors (Host not found, OS error, ...) + Bei der Verbindung zum entfernten Kartenleser ist ein Fehler aufgetreten. Remote device has rejected the connection. Please check the pairing code. + ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) was rejected by the device. Das zu koppelnde Gerät hat die Verbindung verweigert. Überprüfen Sie bitte den Kopplungscode. File not found. + ERROR ALL_PLATFORMS Download of the file failed with HTTP error code 404. Datei nicht gefunden. Cannot save file. + ERROR ALL_PLATFORMS Saving the downloaded file on the local disk failed. Speichern der Datei nicht möglich. Received data were corrupted. + ERROR ALL_PLATFORMS The downloaded file contained unsupported/invalid data. Die empfangenen Daten waren beschädigt. Hash of certificate not in certificate description (issuer: %1). This indicates a misconfiguration or manipulation of the certificate. Please check that your antivirus-software and firewalls are not interfering with SSL traffic. + ERROR ALL_PLATFORMS The TLS certificate was not folded with the Authorization Certificate, thus violating the security requirements. Might also be caused by a firewall and/or the antivirus software. Der Hashwert des Zertifikats ist nicht in der Zertifikatsbeschreibung vorhanden (Aussteller: %1). Dies deutet auf eine Fehlkonfiguration oder Manipulation des Zertifikats hin. Bitte überprüfen Sie, dass weder eine Antivirus-Software noch eine Firewall in den SSL-Verkehr eingreifen. Error while connecting to the server. The SSL certificate uses an unsupported key algorithm or length. Certificate issuer: %1 + ERROR ALL_PLATFORMS Received a TLS certificate that uses an invalid algorithm or key length. Fehler bei der Verbindung zum Server. Der Verschlüsselungsalgorithmus oder die Länge des Schlüssels im SSL-Zertifikat des Servers wird nicht unterstützt. Zertifikatsaussteller: %1 An error occurred. Please contact our %1support%2. - Es ist ein Fehler aufgetreten. Bitte kontaktieren und Sie unseren %1Support%2. + ERROR ANDROID IOS Error message which is used for "masked" errors. Generic message with link to support section of the homepage. + Ein Fehler ist aufgetreten. Bitte kontaktieren Sie unseren %1Support%2. An error occurred. Please contact our %1support%2 or feel free to send us an email. - Es ist ein Fehler aufgetreten. Bitte kontaktieren und Sie unseren %1Support%2 oder senden Sie uns eine Email. + ERROR DESKTOP Error message which is used for "masked" errors. Generic message with link to support section of the homepage. + Ein Fehler ist aufgetreten. Bitte kontaktieren Sie unseren %1Support%2 oder senden Sie uns eine E-Mail. + + + https://www.ausweisapp.bund.de/en/qa/support/ + LABEL ALL_PLATFORMS + https://www.ausweisapp.bund.de/fragen-und-antworten/support/ @@ -4127,7 +6217,7 @@ klicken. The given card access number (CAN) is not correct. You have one more try to enter the correct PIN. Please mind that you have to acknowledge this last try with your card access number (CAN). - Die eingegebene Zugangsnummer (CAN) ist nicht korrekt. Sie haben noch eine weitere Möglichkeit die korrekte PIN einzugeben. Beachten Sie, dass Sie diesen letzten Versuch mit der Zugangsnummer (CAN) bestätigen müssen. + Die eingegebene Zugangsnummer (CAN) ist nicht korrekt. Sie haben noch einen weiteren Versuch die korrekte PIN einzugeben. Beachten Sie, dass Sie diesen letzten Versuch mit der Zugangsnummer (CAN) bestätigen müssen. Wrong PUK @@ -4159,7 +6249,7 @@ klicken. The given PIN is not correct. You have one more try to enter the correct PIN. Please mind that you have to acknowledge this last try with your card access number (CAN). - Die eingegebene PIN ist nicht korrekt. Sie haben noch eine weitere Möglichkeit die korrekte PIN einzugeben. Beachten Sie, dass Sie diesen letzten Versuch mit der Zugangsnummer (CAN) bestätigen müssen. + Die eingegebene PIN ist nicht korrekt. Sie haben noch einen weiteren Versuch die korrekte PIN einzugeben. Beachten Sie, dass Sie diesen letzten Versuch mit der Zugangsnummer (CAN) bestätigen müssen. PIN blocked @@ -4171,7 +6261,15 @@ klicken. The given PIN is not correct. You have 2 remaining tries to enter the correct PIN. - Die eingegebene PIN ist nicht korrekt. Sie haben noch 2 weitere Möglichkeiten die korrekte PIN einzugeben. + Die eingegebene PIN ist nicht korrekt. Sie haben noch 2 weitere Versuche die korrekte PIN einzugeben. + + + PIN successfully unblocked + PIN erfolgreich entsperrt + + + Your ID card is unblocked. You now have three more tries to change your PIN + Ihr Ausweisdokument wurde entsperrt. Sie haben nun drei weitere Versuche um Ihre PIN zu ändern @@ -4224,6 +6322,32 @@ klicken. PDF-Dokumente + + governikus::IosReader + + Scanning process has been finished successfully. + INFO IOS The current session was stopped without errors. + Scannen erfolgreich abgeschlossen. + + + The connection to the ID card has been lost. The process was aborted. + ERROR IOS The card was removed during the communication. + Die Verbindung zum Ausweisdokument wurde unterbrochen. Der Vorgang wird abgebrochen. + + + The connection could not be established. The process was aborted. + ERROR IOS The connection to the card could not be established. + Die Verbindung zum Ausweisdokument konnte nicht hergestellt werden. Der Vorgang wird abgebrochen. + + + + governikus::IosReaderDelegate + + Please place your device on your ID card. + INFO IOS The id card may be inserted, the authentication process may be started. + Bitte platzieren Sie Ihr Gerät über Ihrem Personalausweis. + + governikus::LogFileSaveDialog @@ -4236,7 +6360,7 @@ klicken. An error occurred while saving the file: - Während des Speichervorgangs ist ein Fehler aufgetreten: + Beim Speichern der Datei ist ein Fehler aufgetreten: @@ -4274,6 +6398,7 @@ klicken. governikus::LogHandler An error occurred in log handling: %1 + LABEL ALL_PLATFORMS Es ist ein Fehler bei der Protokollverwaltung aufgetreten: %1 @@ -4281,6 +6406,7 @@ klicken. governikus::LogModel Current log + LABEL ALL_PLATFORMS Aktuelles Protokoll @@ -4289,56 +6415,76 @@ klicken. Send application log per email... + LABEL ANDROID Anwendungsprotokoll per E-Mail senden... Share application log... + LABEL ANDROID Anwendungsprotokoll teilen... support.ausweisapp2@governikus.de support.ausweisapp2@governikus.de - - Android log file - Android Protokolle - <Please describe the error> <Bitte beschreiben Sie den Fehler> + + Mobile log file + Mobile Protokolldatei + + + Could not send log! Please configure your mail client first. + Konnte das Protokoll nicht senden! Bitte konfigurieren Sie Ihr E-Mail-Programm. + + + + governikus::NotificationModel + + hh:mm:ss + HH:mm:ss + governikus::NumberModel The given PIN is not correct. You have 2 tries to enter the correct PIN. - Die eingegebene PIN ist nicht korrekt. Sie haben noch 2 weitere Möglichkeiten die korrekte PIN einzugeben. + INFO ALL_PLATFORMS The wrong PIN was entered on the first attempt. + Die eingegebene PIN ist nicht korrekt. Sie haben noch 2 weitere Versuche die korrekte PIN einzugeben. You have entered the wrong PIN twice. Prior to a third attempt, you have to enter your six-digit card access number first. You can find your card access number on the front of your ID card. + INFO ALL_PLATFORMS The wrong PIN was entered twice, the next attempt requires the CAN for additional verification. Sie haben Ihre PIN zweimal falsch eingegeben. Für einen dritten Versuch müssen Sie vorher Ihre 6-stellige Zugangsnummer eingeben. Sie finden Ihre Zugangsnummer auf der Vorderseite Ihres Ausweises. You have entered a wrong PIN three times. Your PIN is now blocked. You have to enter the PUK now for unblocking. + INFO ALL_PLATFORMS The PIN was entered wrongfully three times, the id card needs to be unlocked using the PUK. Sie haben Ihre PIN dreimal falsch eingegeben. Ihre PIN ist jetzt gesperrt. Zum Entsperren geben Sie bitte Ihre PUK ein. You have entered a wrong CAN, please try again. + INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. Sie haben eine falsche CAN eingegeben. Bitte versuchen Sie es erneut. You have entered a wrong PUK. Please try again. + INFO ALL_PLATFORMS The PUK entered wrongfully and needs to be supplied again. Sie haben eine falsche PUK eingegeben. Bitte versuchen Sie es erneut. governikus::PdfCreator - AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Ministry of the Interior, Building and Community. - Die AusweisApp2 ist ein Produkt der Governikus GmbH & Co. KG - im Auftrag des Bundesministeriums des Innern, für Bau und Heimat. + AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Office for Information Security. + LABEL ALL_PLATFORMS + Die AusweisApp2 ist ein Produkt der Governikus GmbH & Co. KG - im Auftrag des Bundesamtes für Sicherheit in der Informationstechnik. For further information, please see <a href='https://www.ausweisapp.bund.de/'>https://www.ausweisapp.bund.de/</a> + LABEL ALL_PLATFORMS Mehr Information finden Sie auf <a href='https://www.ausweisapp.bund.de/'>https://www.ausweisapp.bund.de/</a> @@ -4346,58 +6492,72 @@ klicken. governikus::PdfExporter Date + LABEL ALL_PLATFORMS Datum Details + LABEL ALL_PLATFORMS Details dd.MM.yyyy hh:mm AP + LABEL ALL_PLATFORMS dd.MM.yyyy HH:mm Provider: + LABEL ALL_PLATFORMS Diensteanbieter: Purpose: + LABEL ALL_PLATFORMS Zweck: Data: + LABEL ALL_PLATFORMS Daten: dd.MM.yyyy + LABEL ALL_PLATFORMS dd.MM.yyyy hh:mm AP + LABEL ALL_PLATFORMS HH:mm At %1 %2 the following data were saved: + LABEL ALL_PLATFORMS Die folgenden Daten wurden hier %1 %2 gespeichert: History + LABEL ALL_PLATFORMS Verlauf Entry + LABEL ALL_PLATFORMS Eintrag Content + LABEL ALL_PLATFORMS Inhalt At %1 %2 the following data has been read out of your ID card: + LABEL ALL_PLATFORMS Die folgenden Daten wurden hier %1 %2 aus Ihrem Ausweisdokument ausgelesen: Information + LABEL ALL_PLATFORMS Information @@ -4436,14 +6596,17 @@ klicken. governikus::ProviderModel %1 seconds free, afterwards + INFO ALL_PLATFORMS Free of charge seconds when calling the hotline. %1 Sekunde frei, danach landline costs %1; + INFO ALL_PLATFORMS Land line charges when calling the hotline. Festnetzpreis %1; mobile costs may vary. + INFO ALL_PLATFORMS Cell phone charges when calling the hotline. Mobilfunkpreise abweichen. @@ -4452,14 +6615,17 @@ klicken. %1/min + INFO ALL_PLATFORMS Unit for expenses for calling the hotline (per minute). %1/min %1/call + INFO ALL_PLATFORMS Unit for expenses for calling the hotline (per call). %1/Anruf %1 EUR + INFO ALL_PLATFORMS Currency unit for expenses for calling the hotline (Euro/Cent). %1 EUR @@ -4489,39 +6655,18 @@ klicken. governikus::ReaderDeviceDialog Reader Driver Integration - Treiber für Kartenlesegeräte + Treiber für Kartenleser governikus::ReaderDeviceWidget - online help - Is embedded in a sentence. - Online-Hilfe - - - No smartphone with enabled remote service found. See %1 for details of use. - Kein Smartphone mit aktiviertem Fernzugriff gefunden. Informationen zur Verwendung befinden sich unter %1. - - - No connected card reader found. See %1 for installation of card readers. - Es wurde kein Kartenlesegerät gefunden. Details zur Installation finden Sie in der %1. - - - Please connect suitable card reader. - Bitte schließen Sie ein geeignetes Kartenlesegerät an. + Please connect a suitable card reader. + Bitte schließen Sie einen geeigneten Kartenleser an. Select a device to display more information about it - Wählen Sie ein Kartenlesegerät aus, um mehr Informationen zu erhalten - - - hh:mm:ss AP - HH:mm:ss - - - The list of card readers was last updated at %1. - Die Liste der Kartenlesegeräte wurde zuletzt um %1 aktualisiert. + Wählen Sie einen Kartenleser aus, um mehr Informationen zu erhalten Please start pairing mode first. @@ -4536,71 +6681,115 @@ klicken. governikus::ReaderDriverModel Driver installed + LABEL ALL_PLATFORMS Treiber installiert No driver installed + LABEL ALL_PLATFORMS Treiber nicht installiert Not connected + LABEL ALL_PLATFORMS Nicht angeschlossen - Device - Gerät + Card reader + LABEL ALL_PLATFORMS + Kartenleser Status + LABEL ALL_PLATFORMS Status Card reader ready for use. - Das Kartenlesegerät kann verwendet werden. + LABEL ALL_PLATFORMS + Der Kartenleser kann verwendet werden. Please download and install the driver you can find at: %1 + INFO ALL_PLATFORMS The driver for card reader needs to be installed, the download link is provided in the message. Bitte installieren Sie den Treiber: %1 + + online help + Is embedded in a sentence. + Online-Hilfe + + + No connected card reader found. See %1 for installation of card readers. + INFO ALL_PLATFORMS No card reader was found, the message contains a link to the installation section of the manual. + Es wurde kein Kartenleser gefunden. Details zur Installation finden Sie in der %1. + + + hh:mm:ss AP + HH:mm:ss + + + The list of card readers was last updated at %1. + LABEL ALL_PLATFORMS + Die Liste der Kartenleser wurde zuletzt um %1 aktualisiert. + governikus::RemoteDeviceModel Not connected + LABEL ALL_PLATFORMS Nicht verbunden - - Paired and available - Gekoppelt und verfügbar - Paired, but unsupported + LABEL ALL_PLATFORMS Gekoppelt, aber nicht unterstützt Paired, but unavailable + LABEL ALL_PLATFORMS Gekoppelt, aber nicht verfügbar - - Unsupported version - Nicht unterstützte Version - Not paired + LABEL ALL_PLATFORMS Nicht gekoppelt Device + LABEL ALL_PLATFORMS Gerät Status + LABEL ALL_PLATFORMS Status dd.MM.yyyy hh:mm AP dd.MM.yyyy HH:mm + + Available + LABEL ALL_PLATFORMS + Verfügbar + + + Unsupported + LABEL ALL_PLATFORMS + Nicht unterstützt + + + online help + Is embedded in a sentence. + Online-Hilfe + + + No smartphone with enabled remote service found. See %1 for details of use. + INFO ALL_PLATFORMS No smartphone with enabled remote service was found on the same network. + Kein Smartphone mit aktiviertem Fernzugriff gefunden. Informationen zur Verwendung befinden sich unter %1. + governikus::RemotePinInputDialog @@ -4617,28 +6806,38 @@ klicken. governikus::RemoteServiceModel NFC is not available on your device. + INFO ALL_PLATFORMS The device does not offer NFC. NFC ist auf Ihrem Gerät nicht verfügbar. Please enable NFC to use the remote service. + INFO ALL_PLATFORMS NFC is available but not active. Bitte aktivieren Sie NFC, um den Fernzugriff zu benutzen. Please connect your WiFi to use the remote service. + INFO ALL_PLATFORMS The wifi feature is not enabled but required to use the smartphone as a card reader (SaK). Bitte verbinden Sie sich mit Ihrem WLAN, um den Fernzugriff zu benutzen. + + Please pay attention to the display on your other device "%1". + INFO ANDROID IOS The smartphone is connected as card reader (SaK) and currently processing an authentication request. The user is asked to pay attention the its screen. + Bitte beachten Sie die Anzeige auf Ihrem anderen Gerät "%1". + governikus::RemoteServiceSettings Remote Reader - Entferntes Kartenlesegeräte + LABEL ALL_PLATFORMS + Entfernter Kartenleser governikus::SelfData This data has not been stored in this chip generation. + INFO ALL_PLATFORMS The requested data is not stored on this chip's generation. Diese Datengruppe wurde in dieser Chip-Generation nicht gespeichert. @@ -4647,50 +6846,62 @@ klicken. Family name + LABEL ALL_PLATFORMS Familienname Birth name + LABEL ALL_PLATFORMS Geburtsname Given name(s) + LABEL ALL_PLATFORMS Vorname(n) Doctoral degree + LABEL ALL_PLATFORMS Doktorgrad Date of birth + LABEL ALL_PLATFORMS Geburtsdatum Place of birth + LABEL ALL_PLATFORMS Geburtsort Address + LABEL ALL_PLATFORMS Adresse Document type + LABEL ALL_PLATFORMS Dokumentenart Nationality + LABEL ALL_PLATFORMS Staatsangehörigkeit Religious / artistic name + LABEL ALL_PLATFORMS Ordens- / Künstlername Issuing country + LABEL ALL_PLATFORMS Ausstellender Staat Residence permit I + LABEL ALL_PLATFORMS Nebenbestimmungen I (nur eAT) @@ -4803,7 +7014,7 @@ klicken. Card Readers - Kartenlesegeräte + Kartenleser Almost done! @@ -4826,14 +7037,47 @@ klicken. governikus::StateChangePin You have successfully changed your PIN. + INFO ALL_PLATFORMS The pin was changed successfully. Sie haben Ihre PIN erfolgreich geändert. - governikus::StateEstablishPaceChannel + governikus::StateCheckRefreshAddress - PIN successfully unblocked - PIN erfolgreich entsperrt + Sending data to service provider + INFO ALL_PLATFORMS Status message after the communication between card and server is completed, the result is being forwarded to the provider. + Sende Daten an den Diensteanbieter + + + + governikus::StateConnectCard + + The online identification function is disabled. + Die Online-Ausweisfunktion ist deaktiviert. + + + + governikus::StateDidAuthenticateEac1 + + Service provider is being verified + INFO ALL_PLATFORMS Status message after the PIN was entered, Terminal Authentication. + Diensteanbieter wird geprüft + + + + governikus::StateDidAuthenticateEac2 + + Card is being verified + INFO ALL_PLATFORMS Status message after the PIN was entered, Card Authentication. + Karte wird geprüft + + + + governikus::StateTransmit + + Reading data + INFO ALL_PLATFORMS Status message after the PIN was entered, communication between eID server and card is running. + Daten werden gelesen @@ -4841,9 +7085,15 @@ klicken. Validity: %1 - %2 + LABEL ALL_PLATFORMS Gültigkeit: %1 - %2 + + Preparing results + INFO ALL_PLATFORMS Status message after the authentication was completed, the results are prepared for the user, mainly relevant for the self authentication since it takes some more time. + Bereite Ergebnisse auf + governikus::StepAdviseUserToRemoveCardGui @@ -4853,7 +7103,7 @@ klicken. You may now remove your ID card from the card reader. - Sie können nun Ihr Ausweisdokument vom Kartenlesegerät entfernen. + Sie können nun Ihr Ausweisdokument vom Kartenleser entfernen. @@ -4864,7 +7114,7 @@ klicken. Please observe the display of your card reader. - Bitte beachten Sie die Anzeige Ihres Kartenlesegeräts. + Bitte beachten Sie die Anzeige Ihres Kartenlesers. @@ -4879,26 +7129,6 @@ klicken. Only digits (0-9) are permitted. Es sind nur Ziffern (0-9) erlaubt. - - Card is being verified - Karte wird geprüft - - - Service provider is verified - Diensteanbieter wird geprüft - - - Reading data - Daten werden gelesen - - - Service provider is being verified - Diensteanbieter wird geprüft - - - Identify now - Jetzt ausweisen - See details under "more..." Weitere Details unter "mehr..." @@ -4933,7 +7163,7 @@ klicken. Please pay attention to the display of your card reader. - Bitte beachten Sie die Anzeige auf Ihrem Kartenlesegerät. + Bitte beachten Sie die Anzeige auf Ihrem Kartenleser. Please enter the six-digit card access number (CAN) for identification. @@ -4979,6 +7209,10 @@ klicken. PIN: PIN: + + Identify now + Jetzt ausweisen + governikus::StepChooseCardGui @@ -4996,15 +7230,15 @@ klicken. No card reader found. Please connect card reader first. - Kein Kartenlesegerät erkannt. Bitte schließen Sie ein Kartenlesegerät an. + Kein Kartenleser erkannt. Bitte schließen Sie einen Kartenleser an. If you need help or have problems with your card reader, you can consult the %1online help%2 for futher information. - Wenn Sie Hilfe benötigen oder Probleme mit Ihrem Kartenlesegerät haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. + Wenn Sie Hilfe benötigen oder Probleme mit Ihrem Kartenleser haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. Please choose "Settings" to install a card reader or configure your smartphone as a card reader. - Falls Sie ein Kartenlesegerät einrichten oder Ihr Smartphone für die Nutzung als Kartenlesegerät konfigurieren möchten, klicken Sie bitte auf "Einstellungen". + Falls Sie einen Kartenleser einrichten oder Ihr Smartphone für die Nutzung als Kartenleser konfigurieren möchten, klicken Sie bitte auf "Einstellungen". Extended Length is not supported. @@ -5012,11 +7246,11 @@ klicken. Your remote reader does not meet the technical requirements (Extended Length not supported). - Ihr entferntes Kartenlesegerät erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). + Ihr entfernter Kartenleser erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). At least one of your card readers does not meet the technical requirements (Extended Length not supported). Please place the ID card on a different card reader. - Mindestens eines Ihrer Kartenlesegeräte erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). Bitte platzieren Sie Ihren Ausweis auf einem anderen Kartenlesegerät. + Mindestens einer Ihrer Kartenleser erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). Bitte platzieren Sie Ihren Ausweis auf einem anderen Kartenleser. Connected to following remote readers: %1. @@ -5036,7 +7270,7 @@ klicken. Please make sure that only one card reader with an ID card on it is connected to your computer. If you have already placed an ID card on your card reader, you can consult the %1online help%2 for futher information. - Bitte stellen Sie sicher, dass an Ihrem Computer nur ein Kartenlesegerät mit aufliegendem Ausweisdokument angeschlossen ist. Sollten Sie bereits ein Ausweisdokument aufgelegt haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. + Bitte stellen Sie sicher, dass an Ihrem Computer nur ein Kartenleser mit aufliegendem Ausweisdokument angeschlossen ist. Sollten Sie bereits ein Ausweisdokument aufgelegt haben, können Sie für weitere Informationen die %1Online-Hilfe%2 öffnen. Online identification function is disabled. @@ -5046,6 +7280,18 @@ klicken. This action cannot be performed. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function. Diese Aktion kann leider nicht durchgeführt werden. Die Online-Ausweisfunktion Ihres Ausweisdokuments ist nicht aktiviert. Bitte wenden Sie sich an die Behörde, die Ihr Ausweisdokument ausgegeben hat, um die Online-Ausweisfunktion zu aktivieren. + + Retry connection + Verbindungsversuch wiederholen + + + Retry? + Erneut versuchen? + + + Weak NFC signal. Please reposition your card. + Schwacher NFC-Empfang. Bitte korrigieren Sie die Position Ihres Ausweises. + governikus::StepErrorGui @@ -5063,29 +7309,13 @@ klicken. Send email - Email versenden + E-Mail senden AusweisApp2 error report - %1 Subject from error report mail AusweisApp2 Fehlerbericht - %1 - - Please describe the error that occurred. - Bitte beschreiben Sie den aufgetretenen Fehler. - - - You may want to attach the logfile which can be saved from the error dialog. - Im Fehlerdialog können Sie eine Logdatei speichern um sie an diese Email anzuhängen. - - - Error code - Fehlercode - - - Critical errors: - Kritische Fehler: - governikus::StepShowSelfAuthenticationDataGui @@ -5094,18 +7324,56 @@ klicken. Schließen + + governikus::SurveyModel + + Vendor + Hersteller + + + Model Name + Modellname + + + Model Number + Modellnummer + + + Build Number + Versionsnummer + + + Android version + Android-Version + + + Kernel version + Kernel-Version + + + Max. NFC Packet Length + Max. NFC Paketlänge + + + AusweisApp2 Version + AusweisApp2-Version + + governikus::TrayIcon Open + LABEL DESKTOP Öffnen Exit AusweisApp2 + LABEL DESKTOP AusweisApp2 beenden AusweisApp2 was started. + LABEL DESKTOP AusweisApp2 wurde gestartet. @@ -5128,58 +7396,72 @@ klicken. governikus::WebserviceActivationContext The browser connection was lost. + ERROR ALL_PLATFORMS No HTTP connection present. Die Verbindung zum Browser ging verloren. Cannot start authentication + ERROR ALL_PLATFORMS A new authentication request was received while the previous one was still running. Part of an HTML error page. Authentisierungsvorgang kann nicht gestartet werden An operation is already in progress. + ERROR ALL_PLATFORMS A new authentication request was received while the previous one was still running. Part of an HTML error page. Ein Vorgang ist bereits in Arbeit. Would you like to try again? + ERROR ALL_PLATFORMS A new authentication request was received while the previous one was still running. Part of an HTML error page. Möchten Sie es erneut versuchen? Try again + ERROR ALL_PLATFORMS A new authentication request was received while the previous one was still running. Part of an HTML error page. Erneut versuchen 400 Bad Request + ERROR ALL_PLATFORMS HTTP error code 400, invalid request, part of an HTML error page. 400 Ungültige Anfrage 404 Not found + ERROR ALL_PLATFORMS HTTP error code 404, invalid request, part of an HTML error page. 404 Nicht gefunden Invalid request + ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page Ungültige Anfrage Your browser sent a request that couldn't be interpreted. + ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page Ihr Browser hat eine Anfrage gesendet, die nicht interpretiert werden konnte. Error message + ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page Fehlermeldung Would you like to report this error? + ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page Möchten Sie diesen Fehler melden? Report now + ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page Jetzt melden The connection to the browser was lost. No forwarding was executed. Please try to call the URL again manually: %1 + ERROR ALL_PLATFORMS The connection to the browser was lost/timed out.. Die Verbindung zum Browser ging verloren. Es konnte keine Weiterleitung durchgeführt werden. Bitte versuchen Sie die URL manuell aufzurufen: %1 https://www.ausweisapp.bund.de/en/qa/report-an-error/ + ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page https://www.ausweisapp.bund.de/fragen-und-antworten/melden-sie-einen-fehler/ @@ -5187,50 +7469,62 @@ klicken. governikus::WebserviceActivationHandler An unknown program uses the required port (%1). Please exit the other program and try again! + ERROR ALL_PLATFORMS An unknown programme is using the local port on which the AA2 listens. Eine unbekannte Anwendung verwendet den benötigten Port (%1) bereits. Bitte beenden Sie zuerst die andere Anwendung und versuchen Sie es anschließend erneut! Another program (%1) uses the required port (%2). Please exit this other program and try again! + ERROR ALL_PLATFORMS A known programme is using the local port on which the AA2 listens. Eine andere Anwendung (%1) verwendet den benötigten Port (%2) bereits. Bitte beenden Sie zuerst die andere Anwendung und versuchen Sie es anschließend erneut! 404 Not found + ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. 404 Nicht gefunden Invalid request + ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. Ungültige Anfrage Your browser sent a request that couldn't be interpreted. + ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. Ihr Browser hat eine Anfrage gesendet, die nicht interpretiert werden konnte. Error message + ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. Fehlermeldung Unknown request: %1 + ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. Unbekannte Anfrage: %1 Would you like to report this error? + ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. Möchten Sie diesen Fehler melden? Report now + ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. Jetzt melden You tried to start a newer version (%1) of currently running application. Please stop the current version (%2) and start again! + ERROR ALL_PLATFORMS The external request to show the UI requested a newer version than the one currently installed. Sie versuchen eine neuere Version (%1) der aktuell laufenden Anwendung zu starten. Bitte beenden Sie zuerst die andere Version (%2) und versuchen Sie es anschließend erneut! You tried to start an older version (%1) of currently running application. Please open the currently running version (%2)! + ERROR ALL_PLATFORMS The external request to show the UI requested an older version than the one currently installed. Sie versuchen eine ältere Version (%1) der aktuell laufenden Anwendung zu starten. Bitte öffnen Sie die aktuell laufende Version (%2)! https://www.ausweisapp.bund.de/en/qa/report-an-error/ + ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. https://www.ausweisapp.bund.de/fragen-und-antworten/melden-sie-einen-fehler/ @@ -5253,6 +7547,14 @@ klicken. Sie können sich auch später ausweisen, indem Sie erneut auf die Internetseite des Diensteanbieters gehen. + + governikus::WorkflowModel + + AusweisApp2 error report - %1 + Subject from error report mail + AusweisApp2 Fehlerbericht - %1 + + governikus::WorkflowQtWidget @@ -5275,7 +7577,68 @@ klicken. main To close the app, quickly press the back button twice. + INFO ANDROID IOS Hint that is shown if the users pressed the "back" button on the top-most navigation level for the first time (a second press closes the app). Um die Anwendung zu schließen, drücken Sie zweimal schnell die Zurück-Taste. + + Text files + LABEL DESKTOP_QML + Textdateien + + + Another application uses %1 + INFO DESKTOP_QML The AA2 is currently remote controlled via the SDK interface, concurrent usage of the AA2 is not possible. + Eine andere Anwendung verwendet %1 + + + The user interface of the %1 is closed. + INFO DESKTOP_QML Header of the popup that is shown when the AA2 is closed for the first time. + Die Benutzeroberfläche der %1 wird geschlossen. + + + The program remains available via the icon in the system tray. Click on the %1 icon to reopen the user interface. + INFO DESKTOP_QML Content of the popup that is shown when the AA2 is closed for the first time. + Das Programm steht weiterhin im Infobereich zur Verfügung. Klicken Sie auf das Symbol der %1, um die Anwendung wieder zu öffnen. + + + Do not show this dialog again. + LABEL DESKTOP_QML + Diesen Hinweis nicht mehr anzeigen. + + + Unsupported version of %1. + INFO DESKTOP_QML Message that the update data is invalid and can't be used. + Nicht unterstützte Version von %1. + + + An update is available (Version: %1). + INFO DESKTOP_QML An update was found which matches the current platform, the new version number is shown in the message. + Eine Aktualisierung ist verfügbar (Version: %1). + + + Download + INFO DESKTOP_QML An update was found. This is the caption of the download button, clicking it opens the link in the browser. + Download + + + Release notes + INFO DESKTOP_QML An update was found. This is the caption of the release note button, clicking it opens the note in the browser. + Aktualisierungshinweise + + + Current version %1 is up to date. + INFO DESKTOP_QML The AA2 is up-to-date, this message is only shown if the update check is started by the user and not via the auto-update functionality. + Version %1 ist aktuell. + + + Developer Mode: Enabled! + LABEL DESKTOP_QML + Entwicklermodus: Aktiviert! + + + Disable + LABEL DESKTOP_QML Global button to disable developer mode. + Deaktivieren + diff --git a/resources/travis/setup.sh b/resources/travis/setup.sh index a34f10f..80a1901 100644 --- a/resources/travis/setup.sh +++ b/resources/travis/setup.sh @@ -5,8 +5,8 @@ set -eu readonly CLONE_DIR="${CLONE_DIR:-$(pwd)}" -MINIROOTFS_VERSION="3.9.0" -MINIROOTFS_SHA="f82efed1a80c9af86c38bed10f3541c5588453b97684d767a5a3b0f3fa0e3f09" +MINIROOTFS_VERSION="3.10.0" +MINIROOTFS_SHA="ec3da7fb5f709a1ce912e6e31fccc5588420c5f1dcecc362c72c989532c1917a" MINIROOTFS="alpine-minirootfs-${MINIROOTFS_VERSION}-x86_64.tar.gz" MINIROOTFS_URI="http://dl-cdn.alpinelinux.org/alpine/v${MINIROOTFS_VERSION%.*}/releases/x86_64/$MINIROOTFS" diff --git a/resources/updatable-files/supported-providers.json b/resources/updatable-files/supported-providers.json index 4abfac8..bc967da 100644 --- a/resources/updatable-files/supported-providers.json +++ b/resources/updatable-files/supported-providers.json @@ -274,13 +274,13 @@ "exclude": ["ios"], "shortName": {"" : "BAföG Online Nordrhein-Westfalen"}, "longDescription": {"": "In Nordrhein-Westfalen können Sie mit der Online-Ausweisfunktion Ihren Antrag auf BAföG für eine Ausbildung (Schulausbildung, Studium in Nordrhein-Westfalen sowie Studium in Belgien, den Niederlanden oder Luxemburg) oder Ihren Antrag auf Aufstiegsfortbildungsförderung (z. B. für die Meisterausbildung) in Nordrhein-Westfalen bequem und schnell im Internet stellen.
Ihr Antrag muss dann nicht mehr ausgedruckt, unterschrieben und versandt werden, sondern gilt mit der Übermittlung Ihrer Daten als wirksam gestellt. Beizufügende und nachzureichende Unterlagen sowie Bescheinigungen können Sie über die Upload-Funktion ebenfalls elektronisch übermitteln.
Nach Übermittlung der Daten wird Ihnen der Antrag als PDF-Dokument zum Download angeboten. Der Bescheid wird Ihnen per Post zugestellt."}, - "address": "https://www.bafoeg-online.nrw.de", - "homepage": "https://www.bafoeg-online.nrw.de", - "phone": "+49 385-588 7145", - "email": "m.boehm@bm.mv-regierung.de", - "postalAddress": "Ministerium für Bildung, Wissenschaft und Kultur Mecklenburg-Vorpommern
Abteilung 1
Referat 140
Werderstraße 124
19055 Schwerin", + "address": "https://www.bafoeg-online.nrw.de/bafoeg/StartNutzung.html", + "homepage": "https://www.bafoeg-online.nrw.de/bafoeg/StartNutzung.html", + "phone": "+49 211 896-04", + "email": " poststelle@miwf.nrw.de", + "postalAddress": "Ministerium für Innovation, Wissenschaft und Forschung des Landes Nordrhein-Westfalen
Besucher- und Lieferanschrift
Völklinger Straße 49
40221 Düsseldorf", "category": "citizen", - "subjectUrls": [] + "subjectUrls": ["https://www.bafoeg-online.nrw.de/bafoeg"] }, { "exclude": ["ios"], @@ -380,7 +380,6 @@ "subjectUrls": [] }, { - "exclude": ["ios"], "shortName": {"" : "Bürgerportal Baden-Württemberg"}, "longDescription": {"": "Das Bürgerportal ,mein service-bw' bietet Bürgerinnen und Bürgern mit dem Personalausweis praktische Anwendungen für die Online-Ausweisfunktion. Das Portal bietet seinen Nutzern unter anderem:
- sicheres Registrieren und Anmelden, um Behördengänge im Internet zu erledigen
- verschlüsselte Ablage persönlicher Daten und Dateien in einem Datenspeicher im Internet, dem sogenannten Dokumentensafe
- orts- und zeitunabhängiger Zugang zu den Daten im Dokumentensafe sowie die Möglichkeit, diese elektronisch an Behörden weiterzuleiten (z. B. für eine Gewerbeanmeldung)."}, "address": "https://service-bw.de/zfinder-bw-web/welcome.do?showMsbwDetails=1", @@ -573,7 +572,6 @@ "subjectUrls": ["https://easy-login.vdg-portal.de"] }, { - "exclude": ["ios"], "shortName": {"" : "ELSTER"}, "longName": {"" : "ELSTER - Die elektronische Steuererklärung"}, "shortDescription": {"": "Abwicklung der Steuererklärungen und -anmeldungen über das Internet."}, @@ -629,15 +627,18 @@ { "exclude": ["ios"], "shortName": {"" : "Hamburg Service Online-Bürgerdienste"}, - "longDescription": {"": "Über das Portal ,HamburgService' finden Sie auf einen Blick alle Online-Dienste der Freien und Hansestadt Hamburg. Für die Dienste, die mit sensiblen Daten arbeiten (Dienste der Sicherheitsstufe 2), müssen Sie sich nach der Registrierung einmalig identifizieren. Sie können dazu die Online-Ausweisfunktion Ihres Personalausweises nutzen. Eine persönliche Identifizierung in einem Kundenzentrum der Stadt Hamburg ist dann nicht mehr nötig."}, - "address": "https://gateway.hamburg.de/HamburgGateway/FVP/Application/Index.aspx", - "homepage": "https://www.hamburg.de/", - "phone": "", + "longName": {"" : "Hamburg Service Online-Bürgerdienste"}, + "shortDescription": {"": "Digitales Dienstleistungsangebot der Freien und Hansestadt Hamburg (FHH)"}, + "longDescription": {"": "Digitales Dienstleistungsangebot der Freien und Hansestadt Hamburg (FHH)"}, + "address": "https://servicekonto.serviceportal.hamburg.de/Servicekonto/Registration/SelectServicekontotype/ShowMenu/", + "homepage": "https://serviceportal.hamburg.de", + "phone": "+49 40 42823-2420", "email": "dataporthamburggateway-service@dataport.de", - "postalAddress": "Bürgermeister Olaf Scholz
Rathausmarkt 1
20095 Hamburg", + "postalAddress": "Freie und Hansestadt Hamburg
Erster Bürgermeister Dr. Peter Tschentscher Rathausmarkt 1
20095 Hamburg", "category": "citizen", - "tcTokenUrlInfo" : "TcToken URL requires valid dynamic request id.", - "subjectUrls": ["https://gateway.hamburg.de"] + "tcTokenUrl": "https://idp.servicekonto.serviceportal.hamburg.de", + "subjectUrls": ["https://idp.servicekonto.serviceportal.hamburg.de"], + "icon": "hamburg_serviceportal_icon.png" }, { "exclude": ["ios"], @@ -747,6 +748,8 @@ "phone": "+49 421 204 95-0", "email": "kontakt@governikus.com", "postalAddress": "Governikus GmbH & Co. KG
Hochschulring 4
28359 Bremen", + "image": "openpgp_image.jpg", + "icon": "openpgp_icon.svg", "category": "other", "tcTokenUrl" : "https://pgp.governikus.de/pgp/ausweis-app-2", "subjectUrls": ["https://pgp.governikus-eid.de"] @@ -754,13 +757,13 @@ { "shortName": {"" : "Selbstauskunft - „Meine Daten einsehen“"}, "longDescription": {"": "Die AusweisApp2 verfügt über die Funktion \"Meine Daten einsehen\". Mit dieser Funktion können die auf dem Personalausweis bzw. dem elektronischen Aufenthaltstitel gespeicherten Daten ausgelesen und angezeigt werden. Hierbei sprechen wir auch von einer sogenannten Selbstauskunft.

Sobald Sie die AusweisApp2 gestartet und ein geeigneter Kartenleser installiert bzw. ein Android-Smartphone verbunden haben, können Sie diese Funktion unter dem Menüpunkt \"Ausweisen\" aufrufen. (Auf Mobilgeräten finden Sie die Ausweis-Auskunft übrigens auch direkt auf der Startseite sowie im Menüeintrag „Ausweisen“.)

Nach Ihrer PIN-Eingabe und erfolgreicher Datenübertragung werden die Daten in der AusweisApp2 dargestellt.

Bei der Selbstauskunft handelt es sich um einen reinen Demonstrationsdienst. Die ausgelesenen Daten werden lediglich zur Anzeige gebracht und nicht weitergegeben.

Bitte beachten Sie, dass Sie auch für diesen Vorgang eine Internetverbindung benötigen. Dies hat folgenden Hintergrund: Der Zugriff auf die Daten des Personalausweises bzw. des elektronischen Aufenthaltstitels ist nur möglich, wenn derjenige, der auf die Daten zugreifen möchte sich selbst zunächst eindeutig identifiziert. Dies geschieht über das sog. Berechtigungszertifikat. Es wird Ihnen immer angezeigt, wer auf Ihre Daten zugreifen möchte. Die Erlaubnis, ein Berechtigungszertifikat zu erhalten, wird einen Diensteanbieter auf Antrag und nach Prüfung bei der Vergabestelle für Berechtigungszertifikate beim Bundesverwaltungsamt erteilt. Um das technische Berechtigungszertifikat anzuzeigen und darüber hinaus eine Gültigkeitsprüfung des Ausweisdokuments durchführen zu können, ist eine Internetverbindung zwingend erforderlich. Aus diesem Grund spricht man auch von der Online-Ausweisfunktion."}, - "address": "https://www.ausweisapp.bund.de/ausweisapp2/ausprobieren-meine-daten-einsehen/", + "address": "https://www.ausweisapp.bund.de/online-ausweisen/meine-daten-auslesen/", "homepage": "https://www.ausweisapp.bund.de/", "phone": "+49 421 - 204 95 995", "email": "support@ausweisapp.de", - "postalAddress": "Governikus GmbH & Co. KG
- im Auftrag des Bundesministeriums des Innern, für Bau und Heimat -
Hochschulring 4
D-28359 Bremen", - "image": "Selbstauskunft.jpg", - "icon": "npa.svg", + "postalAddress": "Governikus GmbH & Co. KG
- im Auftrag des Bundesamtes für Sicherheit in der Informationstechnik -
Hochschulring 4
D-28359 Bremen", + "image": "selbstauskunft_image.jpg", + "icon": "selbstauskunft_icon.svg", "category": "citizen", "tcTokenUrlInfo" : "https://www.autentapp.de/AusweisAuskunft/WebServiceRequesterServlet?mode=xml", "subjectUrls": ["https://www.autentapp.de"] diff --git a/resources/updatable-files/supported-readers.json b/resources/updatable-files/supported-readers.json index 8e09444..a8688d3 100644 --- a/resources/updatable-files/supported-readers.json +++ b/resources/updatable-files/supported-readers.json @@ -238,7 +238,7 @@ { "Platforms": [{"os": "win"}], "DE": "Der Kartenleser funktioniert nur mit der SCL011-nPA-version. Nach der Installation ist ein Neustart erforderlich.", - "EN": "The cardreader works only with the SCL011-nPA-version. A reboot is required after the installation of the driver." + "EN": "The card reader works only with the SCL011-nPA-version. A reboot is required after the installation of the driver." }, { "Platforms": [{"os": "mac"}], @@ -498,11 +498,6 @@ "DE": "Der Kartenleser funktioniert mit dem systemseitig installierten Treiber. Falls Sie jedoch den Treiber von der Webseite des Herstellers installieren möchten, ist anschließend ein Neustart erforderlich.", "EN": "The card reader operates with the driver automatically installed by the system. In case you prefer to install the driver from the manufacturer's webseite, a reboot is required." }, - { - "Platforms":[{"os": "mac", "max": "10.11"}], - "DE": "Der Kartenleser funktioniert nicht mit dem installierten CCID Treiber des Systems. Es existiert kein Treiber vom Hersteller.", - "EN": "The card reader is not compatible with the installed CCID driver of the operating system. The manufacturer does not provide a driver." - }, { "Platforms":[{"os": "mac", "min": "10.12"}], "DE": "Der Kartenleser funktioniert mit dem systemseitig installierten Treiber.", @@ -543,7 +538,7 @@ "VendorId": "0x076B", "ProductId": "0x5421", "Name": "OMNIKEY 5421", - "Pattern": "OMNIKEY CardMan 5x21-CL 0|OMNIKEY CardMan \\(076B:5421\\) 5421(\\(1\\)|\\(2\\))", + "Pattern": "OMNIKEY CardMan 5x21-CL 0|OMNIKEY Smart Card Reader USB 0|OMNIKEY CardMan \\(076B:5421\\) 5421(\\(1\\)|\\(2\\))", "Icon": "img_HID_Omnikey_542x.png", "IconWithNPA": "img_HID_Omnikey_542x_mit_ausweis.png", "Drivers": @@ -645,11 +640,15 @@ [ { "Platforms": [{"os": "win"}], - "URL": "https://supportportal.gemalto.com/csm?id=kb_article_view&sys_kb_id=8f07753f37854fc0cc47261953990e04&sysparm_article=KB0016422" + "URL": "https://supportportal.gemalto.com/csm/?id=kb_article_view&sysparm_article=KB0016422" }, { "Platforms": [{"os": "mac"}], - "URL": "https://supportportal.gemalto.com/csm?id=kb_article_view&sys_kb_id=43a9b1f337c54fc0cc47261953990e8c&sysparm_article=KB0016424" + "URL": "https://supportportal.gemalto.com/csm/?id=kb_article_view&sysparm_article=KB0016424" + }, + { + "Platforms": [{"os": "unknown"}], + "URL": "https://supportportal.gemalto.com/csm/?id=kb_article_view&sysparm_article=KB0016423" } ], "Information": @@ -666,18 +665,22 @@ "VendorId": "0x08E6", "ProductId": "0x5503", "Name": "Prox-DU HID", - "Pattern": "Gemalto Prox(-DU| Dual)( Contactless_| USB PC Link(Reader| Reader)(\\(2\\)|\\(1\\)))", + "Pattern": "Gemalto .*Prox(-DU| Dual)( Contactless_| USB PC Link(Reader| Reader)(\\(2\\)|\\(1\\)))", "Icon": "img_Gemalto_Prox_DU.png", "IconWithNPA": "img_Gemalto_Prox_DU_mit_ausweis.png", "Drivers": [ { "Platforms": [{"os": "win"}], - "URL": "https://supportportal.gemalto.com/csm?id=kb_article_view&sys_kb_id=8f07753f37854fc0cc47261953990e04&sysparm_article=KB0016422" + "URL": "https://supportportal.gemalto.com/csm/?id=kb_article_view&sysparm_article=KB0016422" }, { "Platforms": [{"os": "mac"}], - "URL": "https://supportportal.gemalto.com/csm?id=kb_article_view&sys_kb_id=43a9b1f337c54fc0cc47261953990e8c&sysparm_article=KB0016424" + "URL": "https://supportportal.gemalto.com/csm/?id=kb_article_view&sysparm_article=KB0016424" + }, + { + "Platforms": [{"os": "unknown"}], + "URL": "https://supportportal.gemalto.com/csm/?id=kb_article_view&sysparm_article=KB0016423" } ], "Information": @@ -758,7 +761,7 @@ "Drivers": [ { - "Platforms": [{"os": "win"}, {"os": "unknown"}], + "Platforms": [{"os": "unknown"}], "URL": "https://cherry.de/download/de/download.php" } ], @@ -774,7 +777,8 @@ "DE": "Es ist kein Treiber vom Hersteller vorhanden.", "EN": "There is no driver from the manufacturer." } - ] + ], + "Internal information": "Unter Windows funktioniert nur NFC A. Reader wird nicht als unterstützt markiert, da er nicht zertifiziert ist." }, { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fcb7696..67d2377 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,9 +84,9 @@ IF(IOS) LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/qml) LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/packaging/ios/de.lproj) + LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/packaging/ios/launchscreen.storyboard) LIST(APPEND IOS_RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/translations) LIST(APPEND IOS_RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/config.json) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/qtlogging.ini) # Attention: the file names correspond to values in the Info.plist IF(BUILD_PREVIEW) @@ -99,20 +99,6 @@ IF(IOS) LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/appIcons/${IOS_APPICON_PATH}Images.xcassets) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/Default-568h@2x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage568@2x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage568@3x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage667@2x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage667@3x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage736@2x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage736@3x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage1024@2x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage1024@3x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage1112@2x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage1112@3x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage1366@2x.png) - LIST(APPEND IOS_RESOURCES ${RESOURCES_DIR}/images/iOS/launchImages/launchImage1366@3x.png) - LIST(APPEND IOS_RESOURCES ${RCC}) SET_SOURCE_FILES_PROPERTIES(${RCC} PROPERTIES GENERATED TRUE) ENDIF() @@ -140,15 +126,14 @@ SET_TARGET_PROPERTIES(AusweisApp PROPERTIES MACOSX_BUNDLE_COPYRIGHT "${COPYRIGHT IF(IOS) TARGET_LINK_LIBRARIES(AusweisApp OpenSSL::SSL) # remove this if iOS uses shared libraries - TARGET_LINK_LIBRARIES(AusweisApp -L${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks) TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/plugins/platforms) TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/plugins/imageformats) TARGET_LINK_LIBRARIES(AusweisApp -L${QT_HOST_PREFIX}/lib) TARGET_LINK_LIBRARIES(AusweisApp -lQt5Network -lQt5Gui -lQt5Core -lQt5Bluetooth -lQt5Svg -lQt5Quick -lQt5Qml -lQt5QuickTemplates2 -lQt5QuickControls2) TARGET_LINK_LIBRARIES(AusweisApp -lqtpcre2 -lqtlibpng -lQt5GraphicsSupport -lQt5FontDatabaseSupport -lQt5ClipboardSupport -lqios -lqsvg -lqjpeg -lqtfreetype) - TARGET_LINK_LIBRARIES(AusweisApp "-lc++ -lz -lm -u _qt_registerPlatformPlugin") - TARGET_LINK_LIBRARIES(AusweisApp ${IOS_ASSETSLIBRARY} ${IOS_UIKIT} ${IOS_COREBLUETOOTH} ${IOS_COREFOUNDATION} ${IOS_OPENGLES} ${IOS_FOUNDATION} ${IOS_QUARTZCORE} ${IOS_CORETEXT} ${IOS_COREGRAPHICS} ${IOS_SECURITY} ${IOS_SYSTEMCONFIGURATION} ${IOS_MOBILECORESERVICES} ${IOS_AUDIOTOOLBOX} ${IOS_IMAGEIO}) + TARGET_LINK_LIBRARIES(AusweisApp "-lc++ -lz -lm") + TARGET_LINK_LIBRARIES(AusweisApp ${IOS_ASSETSLIBRARY} ${IOS_UIKIT} ${IOS_COREBLUETOOTH} ${IOS_COREFOUNDATION} ${IOS_OPENGLES} ${IOS_FOUNDATION} ${IOS_QUARTZCORE} ${IOS_CORETEXT} ${IOS_COREGRAPHICS} ${IOS_SECURITY} ${IOS_SYSTEMCONFIGURATION} ${IOS_MOBILECORESERVICES} ${IOS_AUDIOTOOLBOX} ${IOS_IMAGEIO} ${IOS_CORENFC} ${IOS_MESSAGEUI} ${IOS_STOREKIT}) TARGET_LINK_LIBRARIES(AusweisApp -Wl,-e,_qt_main_wrapper) @@ -172,10 +157,9 @@ IF(IOS) SET_TARGET_PROPERTIES(AusweisApp PROPERTIES RESOURCE "${IOS_RESOURCES}") SET_TARGET_PROPERTIES(AusweisApp PROPERTIES XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon") - SET_TARGET_PROPERTIES(AusweisApp PROPERTIES XCODE_ATTRIBUTE_ARCHS "arm64") SET_TARGET_PROPERTIES(AusweisApp PROPERTIES XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2") - SET_TARGET_PROPERTIES(AusweisApp PROPERTIES XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "10.0") - SET_TARGET_PROPERTIES(AusweisApp PROPERTIES XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") + SET_TARGET_PROPERTIES(AusweisApp PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${PACKAGING_DIR}/ios/AusweisApp2.entitlements") + IF(USE_DISTRIBUTION_PROFILE) SET_TARGET_PROPERTIES(AusweisApp PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "iOS Release (Distribution)") SET_TARGET_PROPERTIES(AusweisApp PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Distribution: Governikus GmbH & Co. KG (G7EQCJU4BR)") @@ -209,6 +193,10 @@ IF(ANDROID) ENDIF() ENDIF() +IF(IOS) + TARGET_LINK_LIBRARIES(AusweisApp AusweisAppCardIos) +ENDIF() + IF((ANDROID AND NOT ANDROID_BUILD_AAR) OR IOS) TARGET_LINK_LIBRARIES(AusweisApp AusweisAppCardBluetooth) ELSEIF(LINUX) @@ -231,7 +219,7 @@ ENDIF() IF(DESKTOP) TARGET_LINK_LIBRARIES(AusweisApp AusweisAppCardPcsc AusweisAppCardDrivers AusweisAppActivationWebservice) - TARGET_LINK_LIBRARIES(AusweisApp AusweisAppUiWidget AusweisAppUiCli) + TARGET_LINK_LIBRARIES(AusweisApp AusweisAppUiWidget) ENDIF() TARGET_LINK_LIBRARIES(AusweisApp AusweisAppUiWebsocket) diff --git a/src/CommandLineParser.cpp b/src/CommandLineParser.cpp index 880261f..fa7a4b3 100644 --- a/src/CommandLineParser.cpp +++ b/src/CommandLineParser.cpp @@ -66,7 +66,7 @@ void CommandLineParser::addOptions() void CommandLineParser::parse(QCoreApplication* pApp) { - if (!pApp) + if (pApp == nullptr) { return; } diff --git a/src/activation/customscheme/CustomSchemeActivationContext.h b/src/activation/customscheme/CustomSchemeActivationContext.h index 1c487ad..299129c 100644 --- a/src/activation/customscheme/CustomSchemeActivationContext.h +++ b/src/activation/customscheme/CustomSchemeActivationContext.h @@ -19,7 +19,7 @@ class CustomSchemeActivationContext QUrl mRedirectAddress; public: - CustomSchemeActivationContext(const QUrl& pActivationUrl); + explicit CustomSchemeActivationContext(const QUrl& pActivationUrl); virtual ~CustomSchemeActivationContext() override; QUrl getActivationURL() const override; diff --git a/src/activation/intent/IntentActivationContext.cpp b/src/activation/intent/IntentActivationContext.cpp index d10c2e2..75935a5 100644 --- a/src/activation/intent/IntentActivationContext.cpp +++ b/src/activation/intent/IntentActivationContext.cpp @@ -13,9 +13,10 @@ using namespace governikus; -IntentActivationContext::IntentActivationContext(const QUrl& pActivationUrl) +IntentActivationContext::IntentActivationContext(const QUrl& pActivationUrl, const QString& pReferrer) : ActivationContext() , mActivationUrl(pActivationUrl) + , mReferrer(pReferrer) , mRedirectAddress() { } diff --git a/src/activation/intent/IntentActivationContext.h b/src/activation/intent/IntentActivationContext.h index 298f96a..5faf2ed 100644 --- a/src/activation/intent/IntentActivationContext.h +++ b/src/activation/intent/IntentActivationContext.h @@ -19,10 +19,11 @@ class IntentActivationContext Q_OBJECT const QUrl mActivationUrl; + const QString mReferrer; QUrl mRedirectAddress; public: - IntentActivationContext(const QUrl& pActivationUrl); + explicit IntentActivationContext(const QUrl& pActivationUrl, const QString& pReferrer); virtual ~IntentActivationContext() override; QUrl getActivationURL() const override; diff --git a/src/activation/intent/IntentActivationHandler.cpp b/src/activation/intent/IntentActivationHandler.cpp index 4c83183..d587a8c 100644 --- a/src/activation/intent/IntentActivationHandler.cpp +++ b/src/activation/intent/IntentActivationHandler.cpp @@ -18,11 +18,11 @@ using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(activation) -void IntentActivationHandler::onIntent(const QUrl& pUrl) +void IntentActivationHandler::onIntent(const QUrl& pUrl, const QString& pReferrer) { - qCDebug(activation) << "Got new authentication request"; + qCDebug(activation) << "Got new authentication request by:" << pReferrer; qCDebug(activation) << "Request URL:" << pUrl; - const auto& context = QSharedPointer::create(pUrl); + const auto& context = QSharedPointer::create(pUrl, pReferrer); connect(context.data(), &IntentActivationContext::fireShowUserInformation, this, &ActivationHandler::fireShowUserInformation); Q_EMIT fireAuthenticationRequest(context); } @@ -50,9 +50,10 @@ void IntentActivationHandler::onApplicationActivated() { #ifdef Q_OS_ANDROID const QString& intent = QAndroidJniObject::callStaticObjectMethod("com/governikus/ausweisapp2/MainActivity", "fetchStoredIntent").toString(); + const QString& referrer = QAndroidJniObject::callStaticObjectMethod("com/governikus/ausweisapp2/MainActivity", "fetchStoredReferrer").toString(); if (!intent.isNull()) { - onIntent(intent); + onIntent(intent, referrer); } #endif } diff --git a/src/activation/intent/IntentActivationHandler.h b/src/activation/intent/IntentActivationHandler.h index 3db1d28..1691eac 100644 --- a/src/activation/intent/IntentActivationHandler.h +++ b/src/activation/intent/IntentActivationHandler.h @@ -22,7 +22,7 @@ class IntentActivationHandler Q_INTERFACES(governikus::ActivationHandler) private: - void onIntent(const QUrl& pUrl); + void onIntent(const QUrl& pUrl, const QString& pReferrer); public: IntentActivationHandler() = default; diff --git a/src/activation/intent/MainActivity.java b/src/activation/intent/MainActivity.java index 401d819..b58232f 100644 --- a/src/activation/intent/MainActivity.java +++ b/src/activation/intent/MainActivity.java @@ -4,18 +4,25 @@ package com.governikus.ausweisapp2; +import android.accessibilityservice.AccessibilityServiceInfo; import android.app.Activity; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; -import android.content.res.Configuration; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; import android.nfc.NfcAdapter; import android.nfc.tech.IsoDep; +import android.os.Build; import android.os.Bundle; import android.util.Log; +import android.view.accessibility.AccessibilityManager; +import android.view.Window; import android.view.WindowManager; +import java.util.List; import org.qtproject.qt5.android.bindings.QtActivity; @@ -24,6 +31,7 @@ public class MainActivity extends QtActivity private static final String LOG_TAG = AusweisApp2Service.LOG_TAG; private static Intent cIntent; + private static Uri cReferrer; private static boolean cStartedByAuth; private NfcForegroundDispatcher mNfcForegroundDispatcher; @@ -55,7 +63,7 @@ public class MainActivity extends QtActivity void enable() { - if (mAdapter != null) + if (mAdapter != null && mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { mAdapter.enableForegroundDispatch(mActivity, mPendingIntent, mFilters, mTechLists); } @@ -64,7 +72,7 @@ public class MainActivity extends QtActivity void disable() { - if (mAdapter != null) + if (mAdapter != null && mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { mAdapter.disableForegroundDispatch(mActivity); } @@ -89,6 +97,24 @@ public class MainActivity extends QtActivity } + // required by IntentActivationHandler -> MainActivityAccessor + public static String fetchStoredReferrer() + { + if (cReferrer == null) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) // API 22 + { + Log.e(LOG_TAG, "No stored referrer available, returning null"); + } + return null; + } + + String ref = cReferrer.toString(); + cReferrer = null; + return ref; + } + + public static boolean isStartedByAuth() { return cStartedByAuth; @@ -98,27 +124,41 @@ public class MainActivity extends QtActivity @Override public void onCreate(Bundle savedInstanceState) { - Log.d(LOG_TAG, "onCreate (initial invocation of application): " + getIntent()); + Log.d(LOG_TAG, "onCreate: " + getIntent()); super.onCreate(savedInstanceState); - cIntent = getIntent(); - cStartedByAuth = "android.intent.action.VIEW".equals(cIntent.getAction()); + + // Make statusbar transparent + Window window = getWindow(); + window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.setStatusBarColor(Color.TRANSPARENT); + + onNewIntent(getIntent()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) // API 22 + { + cReferrer = getReferrer(); + } // register the broadcast receiver after loading the C++ library in super.onCreate() AndroidBluetoothReceiver.register(this); mNfcForegroundDispatcher = new NfcForegroundDispatcher(this); - setRequestedOrientation(isTablet() ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + setRequestedOrientation(isTablet() ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } @Override protected void onNewIntent(Intent newIntent) { - Log.d(LOG_TAG, "onNewIntent (subsequent invocation of application): " + newIntent); + Log.d(LOG_TAG, "onNewIntent: " + newIntent); super.onNewIntent(newIntent); cIntent = newIntent; cStartedByAuth = "android.intent.action.VIEW".equals(cIntent.getAction()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) // API 22 + { + cReferrer = getReferrer(); + } } @@ -168,11 +208,40 @@ public class MainActivity extends QtActivity public boolean isTablet() { final Context context = getBaseContext(); - final int screenLayout = context.getResources().getConfiguration().screenLayout; - final boolean xlarge = (screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; - final boolean large = (screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE; + // https://developer.android.com/training/multiscreen/screensizes.html#TaskUseSWQuali + return context.getResources().getConfiguration().smallestScreenWidthDp >= 600; + } - return xlarge || large; + + public boolean isScreenReaderRunning() + { + AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE); + List services = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN); + return !services.isEmpty(); + } + + + public int getStatusBarHeight() + { + return getDimension("status_bar_height"); + } + + + public int getNavigationBarHeight() + { + return getDimension("navigation_bar_height"); + } + + + private int getDimension(String dimensionName) + { + int result = 0; + int resourceId = getResources().getIdentifier(dimensionName, "dimen", "android"); + if (resourceId > 0) + { + result = getResources().getDimensionPixelSize(resourceId); + } + return result; } diff --git a/src/activation/internal/InternalActivationContext.h b/src/activation/internal/InternalActivationContext.h index 63dafb6..b0c7bb9 100644 --- a/src/activation/internal/InternalActivationContext.h +++ b/src/activation/internal/InternalActivationContext.h @@ -18,7 +18,7 @@ class InternalActivationContext const QUrl mTcTokenUrl; public: - InternalActivationContext(const QUrl& pUrl); + explicit InternalActivationContext(const QUrl& pUrl); virtual ~InternalActivationContext() override = default; QUrl getActivationURL() const override; diff --git a/src/activation/webservice/Template.h b/src/activation/webservice/Template.h index 4d575ff..5901390 100644 --- a/src/activation/webservice/Template.h +++ b/src/activation/webservice/Template.h @@ -31,7 +31,7 @@ class Template /*! * \brief Construct the template \a pTemplate */ - Template(const QString& pTemplate); + explicit Template(const QString& pTemplate); /*! * \brief Get template context keys, i.e. the possible keys diff --git a/src/activation/webservice/WebserviceActivationContext.cpp b/src/activation/webservice/WebserviceActivationContext.cpp index b055193..2571c26 100644 --- a/src/activation/webservice/WebserviceActivationContext.cpp +++ b/src/activation/webservice/WebserviceActivationContext.cpp @@ -41,6 +41,7 @@ bool WebserviceActivationContext::sendProcessing() { if (!mRequest->isConnected()) { + //: ERROR ALL_PLATFORMS No HTTP connection present. mSendError = tr("The browser connection was lost."); return false; } @@ -54,16 +55,22 @@ bool WebserviceActivationContext::sendOperationAlreadyActive() { if (!mRequest->isConnected()) { + //: ERROR ALL_PLATFORMS No HTTP connection present. mSendError = tr("The browser connection was lost."); return false; } Template htmlTemplate = Template::fromFile(QStringLiteral(":/html_templates/alreadyactive.html")); + //: ERROR ALL_PLATFORMS A new authentication request was received while the previous one was still running. Part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("TITLE"), tr("Cannot start authentication")); + //: ERROR ALL_PLATFORMS A new authentication request was received while the previous one was still running. Part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("MESSAGE_HEADER"), tr("Cannot start authentication")); + //: ERROR ALL_PLATFORMS A new authentication request was received while the previous one was still running. Part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("MESSAGE_HEADER_EXPLANATION"), tr("An operation is already in progress.")); + //: ERROR ALL_PLATFORMS A new authentication request was received while the previous one was still running. Part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("CONTENT_HEADER"), tr("Would you like to try again?")); htmlTemplate.setContextParameter(QStringLiteral("CONTENT_LINK"), mRequest->getUrl().toString()); + //: ERROR ALL_PLATFORMS A new authentication request was received while the previous one was still running. Part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("CONTENT_BUTTON"), tr("Try again")); QByteArray htmlPage = htmlTemplate.render().toUtf8(); @@ -79,6 +86,7 @@ bool WebserviceActivationContext::sendErrorPage(http_status pStatusCode, const G { if (!mRequest->isConnected()) { + //: ERROR ALL_PLATFORMS No HTTP connection present. mSendError = tr("The browser connection was lost."); return false; } @@ -88,21 +96,29 @@ bool WebserviceActivationContext::sendErrorPage(http_status pStatusCode, const G QString statusCodeString; if (pStatusCode == HTTP_STATUS_BAD_REQUEST) { + //: ERROR ALL_PLATFORMS HTTP error code 400, invalid request, part of an HTML error page. statusCodeString = tr("400 Bad Request"); } else if (pStatusCode == HTTP_STATUS_NOT_FOUND) { + //: ERROR ALL_PLATFORMS HTTP error code 404, invalid request, part of an HTML error page. statusCodeString = tr("404 Not found"); } Template htmlTemplate = Template::fromFile(QStringLiteral(":/html_templates/error.html")); htmlTemplate.setContextParameter(QStringLiteral("TITLE"), statusCodeString); + //: ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page htmlTemplate.setContextParameter(QStringLiteral("MESSAGE_HEADER"), tr("Invalid request")); + //: ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page htmlTemplate.setContextParameter(QStringLiteral("MESSAGE_HEADER_EXPLANATION"), tr("Your browser sent a request that couldn't be interpreted.")); + //: ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page htmlTemplate.setContextParameter(QStringLiteral("ERROR_MESSAGE_LABEL"), tr("Error message")); htmlTemplate.setContextParameter(QStringLiteral("ERROR_MESSAGE"), pStatus.toErrorDescription(true)); + //: ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page htmlTemplate.setContextParameter(QStringLiteral("REPORT_HEADER"), tr("Would you like to report this error?")); + //: ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page htmlTemplate.setContextParameter(QStringLiteral("REPORT_LINK"), tr("https://www.ausweisapp.bund.de/en/qa/report-an-error/")); + //: ERROR ALL_PLATFORMS Invalid request by the browser, part of an HTML error page htmlTemplate.setContextParameter(QStringLiteral("REPORT_BUTTON"), tr("Report now")); QByteArray htmlPage = htmlTemplate.render().toUtf8(); @@ -123,6 +139,7 @@ bool WebserviceActivationContext::sendRedirect(const QUrl& pRedirectAddress, con if (!mRequest->isConnected()) { const auto& url = QStringLiteral("%2").arg(redirectAddressWithResult.toString(), redirectAddressWithResult.host()); + //: ERROR ALL_PLATFORMS The connection to the browser was lost/timed out.. mSendError = tr("The connection to the browser was lost. No forwarding was executed. Please try to call the URL again manually: %1").arg(url); return false; } diff --git a/src/activation/webservice/WebserviceActivationContext.h b/src/activation/webservice/WebserviceActivationContext.h index ed80848..168e315 100644 --- a/src/activation/webservice/WebserviceActivationContext.h +++ b/src/activation/webservice/WebserviceActivationContext.h @@ -9,7 +9,6 @@ #include -class test_WebserviceActivationContext; namespace governikus { @@ -18,14 +17,13 @@ class WebserviceActivationContext : public ActivationContext { Q_OBJECT - friend class ::test_WebserviceActivationContext; const QSharedPointer mRequest; void setCommonHeaders(HttpResponse& pResponse); public: - WebserviceActivationContext(const QSharedPointer& pRequest); + explicit WebserviceActivationContext(const QSharedPointer& pRequest); virtual ~WebserviceActivationContext() override = default; diff --git a/src/activation/webservice/WebserviceActivationHandler.cpp b/src/activation/webservice/WebserviceActivationHandler.cpp index 897dfb9..cc2a730 100644 --- a/src/activation/webservice/WebserviceActivationHandler.cpp +++ b/src/activation/webservice/WebserviceActivationHandler.cpp @@ -64,8 +64,13 @@ bool WebserviceActivationHandler::start() { qCCritical(activation) << "Cannot start application. Port on localhost is already bound by another program:" << serverAppName; - QString msg = serverAppName.isEmpty() ? tr("An unknown program uses the required port (%1). Please exit the other program and try again!").arg(port) : - tr("Another program (%1) uses the required port (%2). Please exit this other program and try again!").arg(serverAppName).arg(port); + //: ERROR ALL_PLATFORMS An unknown programme is using the local port on which the AA2 listens. + QString msg = tr("An unknown program uses the required port (%1). Please exit the other program and try again!").arg(port); + if (!serverAppName.isEmpty()) + { + //: ERROR ALL_PLATFORMS A known programme is using the local port on which the AA2 listens. + msg = tr("Another program (%1) uses the required port (%2). Please exit this other program and try again!").arg(serverAppName).arg(port); + } Q_EMIT fireShowUserInformation(msg); } @@ -116,13 +121,21 @@ void WebserviceActivationHandler::onNewRequest(const QSharedPointer qCWarning(activation) << "Request type: unknown"; Template htmlTemplate = Template::fromFile(QStringLiteral(":/html_templates/error.html")); + //: ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("TITLE"), tr("404 Not found")); + //: ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("MESSAGE_HEADER"), tr("Invalid request")); + //: ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("MESSAGE_HEADER_EXPLANATION"), tr("Your browser sent a request that couldn't be interpreted.")); + //: ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("ERROR_MESSAGE_LABEL"), tr("Error message")); + //: ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("ERROR_MESSAGE"), tr("Unknown request: %1").arg(url.toString())); + //: ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("REPORT_HEADER"), tr("Would you like to report this error?")); + //: ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("REPORT_LINK"), tr("https://www.ausweisapp.bund.de/en/qa/report-an-error/")); + //: ERROR ALL_PLATFORMS The broweser sent an unknown or faulty request, part of an HTML error page. htmlTemplate.setContextParameter(QStringLiteral("REPORT_BUTTON"), tr("Report now")); QByteArray htmlPage = htmlTemplate.render().toUtf8(); @@ -146,12 +159,14 @@ void WebserviceActivationHandler::handleShowUiRequest(UiModule pUiModule, const if (callerVersion > VersionNumber::getApplicationVersion()) { qCWarning(activation) << "Current version is lower than caller version"; + //: ERROR ALL_PLATFORMS The external request to show the UI requested a newer version than the one currently installed. Q_EMIT fireShowUserInformation(tr("You tried to start a newer version (%1) of currently running application. Please stop the current version (%2) and start again!").arg(version, QCoreApplication::applicationVersion())); return; } else if (callerVersion < VersionNumber::getApplicationVersion()) { qCWarning(activation) << "Current version is higher than caller version"; + //: ERROR ALL_PLATFORMS The external request to show the UI requested an older version than the one currently installed. Q_EMIT fireShowUserInformation(tr("You tried to start an older version (%1) of currently running application. Please open the currently running version (%2)!").arg(version, QCoreApplication::applicationVersion())); return; } diff --git a/src/card/CMakeLists.txt b/src/card/CMakeLists.txt index 1ba6fb9..c23e30c 100644 --- a/src/card/CMakeLists.txt +++ b/src/card/CMakeLists.txt @@ -19,6 +19,10 @@ IF(TARGET Qt5::Nfc) ADD_SUBDIRECTORY(nfc) ENDIF() +IF(IOS) + ADD_SUBDIRECTORY(ios) +ENDIF() + IF(TARGET Qt5::Bluetooth) ADD_SUBDIRECTORY(bluetooth) ENDIF() diff --git a/src/card/base/Apdu.cpp b/src/card/base/Apdu.cpp index 358f27f..fa45ed4 100644 --- a/src/card/base/Apdu.cpp +++ b/src/card/base/Apdu.cpp @@ -22,6 +22,12 @@ int Apdu::length() const } +bool Apdu::isEmpty() const +{ + return mBuffer.isEmpty(); +} + + const QByteArray& Apdu::getBuffer() const { return mBuffer; diff --git a/src/card/base/Apdu.h b/src/card/base/Apdu.h index a5787ad..60dad01 100644 --- a/src/card/base/Apdu.h +++ b/src/card/base/Apdu.h @@ -14,12 +14,13 @@ class Apdu protected: QByteArray mBuffer; - Apdu(const QByteArray& pBuffer); + explicit Apdu(const QByteArray& pBuffer); ~Apdu() = default; public: const QByteArray& getBuffer() const; int length() const; + bool isEmpty() const; }; diff --git a/src/card/base/Card.cpp b/src/card/base/Card.cpp index 1a12ec2..97b21a8 100644 --- a/src/card/base/Card.cpp +++ b/src/card/base/Card.cpp @@ -16,13 +16,18 @@ Card::Card() } -CardReturnCode Card::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) +void Card::setProgressMessage(const QString& pMessage) { - Q_UNUSED(pPasswordId); - Q_UNUSED(pChat); - Q_UNUSED(pCertificateDescription); - Q_UNUSED(pChannelOutput); - Q_UNUSED(pTimeoutSeconds); + Q_UNUSED(pMessage); +} + + +EstablishPaceChannelOutput Card::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, quint8 pTimeoutSeconds) +{ + Q_UNUSED(pPasswordId) + Q_UNUSED(pChat) + Q_UNUSED(pCertificateDescription) + Q_UNUSED(pTimeoutSeconds) qCWarning(card) << "Establishment of PACE channel not supported"; return CardReturnCode::COMMAND_FAILED; } @@ -37,8 +42,8 @@ CardReturnCode Card::destroyPaceChannel() CardReturnCode Card::setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) { - Q_UNUSED(pTimeoutSeconds); - Q_UNUSED(pResponseApdu); + Q_UNUSED(pTimeoutSeconds) + Q_UNUSED(pResponseApdu) qCWarning(card) << "Setting eID PIN is not supported"; return CardReturnCode::COMMAND_FAILED; diff --git a/src/card/base/Card.h b/src/card/base/Card.h index 95b4bdb..2d6675e 100644 --- a/src/card/base/Card.h +++ b/src/card/base/Card.h @@ -8,7 +8,6 @@ #include "CardReturnCode.h" #include "CommandApdu.h" -#include "Commands.h" #include "EstablishPaceChannelOutput.h" #include "ResponseApdu.h" #include "SmartCardDefinitions.h" @@ -44,6 +43,12 @@ class Card */ virtual bool isConnected() = 0; + /*! + * Sets the current workflow progress message. This is necessary for platforms like iOS, + * where interacting with a card leads to a dialog where the message needs to be updated. + */ + virtual void setProgressMessage(const QString& pMessage); + /*! * Performs a transmit to the smart card. * The command APDU buffer is transmitted to the card. @@ -54,7 +59,7 @@ class Card /*! * Establishes a PACE channel, i.e. the corresponding reader is no basic reader. */ - virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds = 60); + virtual EstablishPaceChannelOutput establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, quint8 pTimeoutSeconds = 60); /*! * Destroys an existing PACE channel, i.e. the corresponding reader is no basic reader. diff --git a/src/card/base/CardConnection.cpp b/src/card/base/CardConnection.cpp index eb8706b..253ed1c 100644 --- a/src/card/base/CardConnection.cpp +++ b/src/card/base/CardConnection.cpp @@ -48,6 +48,14 @@ bool CardConnection::getPacePinSuccessful() const } +void CardConnection::setProgressMessage(const QString& pMessage) +{ + QMetaObject::invokeMethod(mCardConnectionWorker.data(), [ = ] { + mCardConnectionWorker->setProgressMessage(pMessage); + }, Qt::QueuedConnection); +} + + bool CardConnection::stopSecureMessaging() { bool result; diff --git a/src/card/base/CardConnection.h b/src/card/base/CardConnection.h index 7bc8dd8..6eebc9e 100644 --- a/src/card/base/CardConnection.h +++ b/src/card/base/CardConnection.h @@ -11,7 +11,6 @@ #include "asn1/CVCertificateChain.h" #include "command/BaseCardCommand.h" #include "CardConnectionWorker.h" -#include "Commands.h" #include "InputAPDUInfo.h" #include "ReaderInfo.h" #include "SmartCardDefinitions.h" @@ -28,11 +27,8 @@ #include -class test_WorkflowContext; -class test_SelfAuthModel; class test_CardConnection; -class test_StateEstablishPaceChannel; - +class test_StatePreparePace; namespace governikus { @@ -44,10 +40,7 @@ class CardConnection : public QObject { private: - friend class ::test_WorkflowContext; - friend class ::test_SelfAuthModel; friend class ::test_CardConnection; - friend class ::test_StateEstablishPaceChannel; Q_OBJECT @@ -87,7 +80,7 @@ class CardConnection } else { - qCritical() << "Cannot invoke card command:" << pCommand->metaObject()->className(); + qCCritical(card) << "Cannot invoke card command:" << pCommand->metaObject()->className(); pCommand->deleteLater(); } @@ -102,7 +95,7 @@ class CardConnection CardConnection(); public: - CardConnection(const QSharedPointer& pCardConnectionWorker); + explicit CardConnection(const QSharedPointer& pCardConnectionWorker); /*! * Destroys the CardConnection and disconnects from the card. @@ -120,6 +113,7 @@ class CardConnection bool getPaceCanSuccessful() const; bool getPacePinSuccessful() const; + void setProgressMessage(const QString& pMessage); bool stopSecureMessaging(); template diff --git a/src/card/base/CardConnectionWorker.cpp b/src/card/base/CardConnectionWorker.cpp index 2e98041..09f09a2 100644 --- a/src/card/base/CardConnectionWorker.cpp +++ b/src/card/base/CardConnectionWorker.cpp @@ -3,7 +3,12 @@ */ #include "CardConnectionWorker.h" + +#include "MSEBuilder.h" #include "pace/PaceHandler.h" +#include "ReadBinaryBuilder.h" +#include "ResetRetryCounterBuilder.h" +#include "SelectBuilder.h" #include @@ -26,9 +31,10 @@ CardConnectionWorker::CardConnectionWorker(Reader* pReader) CardConnectionWorker::~CardConnectionWorker() { - if (hasCard() && mReader->getCard()->isConnected()) + const auto card = mReader ? mReader->getCard() : nullptr; + if (card && card->isConnected()) { - mReader->getCard()->disconnect(); + card->disconnect(); } } @@ -51,12 +57,6 @@ void CardConnectionWorker::setPukInoperative() } -bool CardConnectionWorker::hasCard() const -{ - return !mReader.isNull() && mReader->getCard() != nullptr; -} - - QSharedPointer CardConnectionWorker::getEfCardAccess() const { return getReaderInfo().getCardInfo().getEfCardAccess(); @@ -72,7 +72,8 @@ void CardConnectionWorker::onReaderInfoChanged(const QString& pReaderName) CardReturnCode CardConnectionWorker::transmit(const CommandApdu& pCommandApdu, ResponseApdu& pResponseApdu) { - if (!hasCard()) + const auto card = mReader ? mReader->getCard() : nullptr; + if (!card) { return CardReturnCode::CARD_NOT_FOUND; } @@ -88,15 +89,19 @@ CardReturnCode CardConnectionWorker::transmit(const CommandApdu& pCommandApdu, R } ResponseApdu securedResponseApdu; - returnCode = mReader->getCard()->transmit(securedCommandApdu, securedResponseApdu); - if (!mSecureMessaging->decrypt(securedResponseApdu, pResponseApdu)) + returnCode = card->transmit(securedCommandApdu, securedResponseApdu); + pResponseApdu = mSecureMessaging->decrypt(securedResponseApdu); + if (pResponseApdu.isEmpty()) { + qCDebug(::card) << "Stopping Secure Messaging since it failed. The channel therefore must no be re-used."; + stopSecureMessaging(); + return CardReturnCode::COMMAND_FAILED; } } else { - returnCode = mReader->getCard()->transmit(pCommandApdu, pResponseApdu); + returnCode = card->transmit(pCommandApdu, pResponseApdu); } return returnCode; @@ -105,7 +110,7 @@ CardReturnCode CardConnectionWorker::transmit(const CommandApdu& pCommandApdu, R CardReturnCode CardConnectionWorker::readFile(const FileRef& pFileRef, QByteArray& pFileContent) { - if (!hasCard()) + if (!mReader || !mReader->getCard()) { return CardReturnCode::CARD_NOT_FOUND; } @@ -144,6 +149,16 @@ CardReturnCode CardConnectionWorker::readFile(const FileRef& pFileRef, QByteArra } +void CardConnectionWorker::setProgressMessage(const QString& pMessage) +{ + const auto card = mReader ? mReader->getCard() : nullptr; + if (card) + { + card->setProgressMessage(pMessage); + } +} + + bool CardConnectionWorker::stopSecureMessaging() { if (mSecureMessaging.isNull()) @@ -156,26 +171,25 @@ bool CardConnectionWorker::stopSecureMessaging() } -CardReturnCode CardConnectionWorker::establishPaceChannel(PacePasswordId pPasswordId, - const QString& pPasswordValue, - EstablishPaceChannelOutput& pChannelOutput) +EstablishPaceChannelOutput CardConnectionWorker::establishPaceChannel(PacePasswordId pPasswordId, + const QString& pPasswordValue) { - return establishPaceChannel(pPasswordId, pPasswordValue, nullptr, nullptr, pChannelOutput); + return establishPaceChannel(pPasswordId, pPasswordValue, nullptr, nullptr); } -CardReturnCode CardConnectionWorker::establishPaceChannel(PacePasswordId pPasswordId, +EstablishPaceChannelOutput CardConnectionWorker::establishPaceChannel(PacePasswordId pPasswordId, const QString& pPasswordValue, const QByteArray& pChat, - const QByteArray& pCertificateDescription, - EstablishPaceChannelOutput& pChannelOutput) + const QByteArray& pCertificateDescription) { - if (!hasCard()) + const auto card = mReader ? mReader->getCard() : nullptr; + if (!card) { - pChannelOutput.setPaceReturnCode(CardReturnCode::CARD_NOT_FOUND); return CardReturnCode::CARD_NOT_FOUND; } - CardReturnCode returnCode; + + EstablishPaceChannelOutput output; qCInfo(support) << "Starting PACE for" << pPasswordId; if (mReader->getReaderInfo().isBasicReader()) @@ -183,35 +197,35 @@ CardReturnCode CardConnectionWorker::establishPaceChannel(PacePasswordId pPasswo Q_ASSERT(!pPasswordValue.isEmpty()); PaceHandler paceHandler(sharedFromThis()); paceHandler.setChat(pChat); - returnCode = paceHandler.establishPaceChannel(pPasswordId, pPasswordValue); - pChannelOutput.setPaceReturnCode(returnCode); - pChannelOutput.setStatusMseSetAt(paceHandler.getStatusMseSetAt()); + const auto returnCode = paceHandler.establishPaceChannel(pPasswordId, pPasswordValue); + output.setPaceReturnCode(returnCode); + output.setStatusMseSetAt(paceHandler.getStatusMseSetAt()); if (returnCode == CardReturnCode::OK) { - pChannelOutput.setCarCurr(paceHandler.getCarCurr()); - pChannelOutput.setCarPrev(paceHandler.getCarPrev()); - pChannelOutput.setIdIcc(paceHandler.getIdIcc()); - pChannelOutput.setEfCardAccess(getEfCardAccess()->getContentBytes()); - pChannelOutput.setPaceReturnCode(CardReturnCode::OK); + output.setCarCurr(paceHandler.getCarCurr()); + output.setCarPrev(paceHandler.getCarPrev()); + output.setIdIcc(paceHandler.getIdIcc()); + output.setEfCardAccess(getEfCardAccess()->getContentBytes()); + output.setPaceReturnCode(CardReturnCode::OK); mSecureMessaging.reset(new SecureMessaging(paceHandler.getPaceProtocol(), paceHandler.getEncryptionKey(), paceHandler.getMacKey())); } } else { Q_ASSERT(pPasswordValue.isNull()); - returnCode = mReader->getCard()->establishPaceChannel(pPasswordId, pChat, pCertificateDescription, pChannelOutput); - pChannelOutput.setPaceReturnCode(returnCode); + output = card->establishPaceChannel(pPasswordId, pChat, pCertificateDescription); } - qCInfo(support) << "Finished PACE for" << pPasswordId << "with result" << returnCode; - return returnCode; + qCInfo(support) << "Finished PACE for" << pPasswordId << "with result" << output.getPaceReturnCode(); + return output; } CardReturnCode CardConnectionWorker::destroyPaceChannel() { - if (!hasCard()) + const auto card = mReader ? mReader->getCard() : nullptr; + if (!card) { return CardReturnCode::CARD_NOT_FOUND; } @@ -222,20 +236,21 @@ CardReturnCode CardConnectionWorker::destroyPaceChannel() stopSecureMessaging(); MSEBuilder builder(MSEBuilder::P1::ERASE, MSEBuilder::P2::DEFAULT_CHANNEL); ResponseApdu response; - CardReturnCode cardReturnCode = mReader->getCard()->transmit(builder.build(), response); - qCDebug(card) << "Destroying PACE channel with invalid command causing 6700 as return code"; + CardReturnCode cardReturnCode = card->transmit(builder.build(), response); + qCDebug(::card) << "Destroying PACE channel with invalid command causing 6700 as return code"; return cardReturnCode; } else { - return mReader->getCard()->destroyPaceChannel(); + return card->destroyPaceChannel(); } } CardReturnCode CardConnectionWorker::setEidPin(const QString& pNewPin, quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) { - if (!hasCard()) + const auto card = mReader ? mReader->getCard() : nullptr; + if (!card) { return CardReturnCode::CARD_NOT_FOUND; } @@ -246,7 +261,7 @@ CardReturnCode CardConnectionWorker::setEidPin(const QString& pNewPin, quint8 pT ResetRetryCounterBuilder commandBuilder(pNewPin.toUtf8()); if (transmit(commandBuilder.build(), pResponseApdu) != CardReturnCode::OK || pResponseApdu.getReturnCode() != StatusCode::SUCCESS) { - qCWarning(card) << "Modify PIN failed"; + qCWarning(::card) << "Modify PIN failed"; return CardReturnCode::COMMAND_FAILED; } return CardReturnCode::OK; @@ -254,14 +269,14 @@ CardReturnCode CardConnectionWorker::setEidPin(const QString& pNewPin, quint8 pT else { Q_ASSERT(pNewPin.isEmpty()); - return mReader->getCard()->setEidPin(pTimeoutSeconds, pResponseApdu); + return card->setEidPin(pTimeoutSeconds, pResponseApdu); } } CardReturnCode CardConnectionWorker::updateRetryCounter() { - if (!hasCard()) + if (!mReader || !mReader->getCard()) { return CardReturnCode::CARD_NOT_FOUND; } diff --git a/src/card/base/CardConnectionWorker.h b/src/card/base/CardConnectionWorker.h index 9a7e527..945d269 100644 --- a/src/card/base/CardConnectionWorker.h +++ b/src/card/base/CardConnectionWorker.h @@ -9,7 +9,6 @@ #include "asn1/SecurityInfos.h" #include "CardReturnCode.h" #include "CommandApdu.h" -#include "Commands.h" #include "EstablishPaceChannel.h" #include "FileRef.h" #include "pace/SecureMessaging.h" @@ -42,7 +41,6 @@ class CardConnectionWorker */ QScopedPointer mSecureMessaging; - bool hasCard() const; inline QSharedPointer getEfCardAccess() const; private Q_SLOTS: @@ -53,7 +51,7 @@ class CardConnectionWorker * The Card hold by the Reader is expected to be connected. * The connection is closed, when the CardConnection is destroyed. */ - CardConnectionWorker(Reader* pReader); + explicit CardConnectionWorker(Reader* pReader); /*! * Destroys the CardConnection and disconnects from the card. @@ -78,26 +76,30 @@ class CardConnectionWorker * If the Reader is a basic reader and the PACE channel is successfully established, the subsequent transmits will be secured using, secure messaging. * I. e., a secure messaging channel is established. */ - virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, - const QString& pPasswordValue, - EstablishPaceChannelOutput& pChannelOutput); + virtual EstablishPaceChannelOutput establishPaceChannel(PacePasswordId pPasswordId, + const QString& pPasswordValue); /*! * Performs PACE and establishes a PACE channel for later terminal authentication. * If the Reader is a basic reader and the PACE channel is successfully established, the subsequent transmits will be secured using, secure messaging. * I. e., a secure messaging channel is established. */ - virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, + virtual EstablishPaceChannelOutput establishPaceChannel(PacePasswordId pPasswordId, const QString& pPasswordValue, const QByteArray& pChat, - const QByteArray& pCertificateDescription, - EstablishPaceChannelOutput& pChannelOutput); + const QByteArray& pCertificateDescription); /*! * Destroys a previously established PACE channel. */ virtual CardReturnCode destroyPaceChannel(); + /*! + * Sets the current workflow progress message. This is necessary for platforms like iOS, + * where interacting with a card leads to a dialog where the message needs to be updated. + */ + virtual void setProgressMessage(const QString& pMessage); + /*! * Destroys an established secure messaging channel, if there is one. */ diff --git a/src/card/base/CardInfo.cpp b/src/card/base/CardInfo.cpp index c7ffe62..4a13cb4 100644 --- a/src/card/base/CardInfo.cpp +++ b/src/card/base/CardInfo.cpp @@ -9,6 +9,7 @@ #include "asn1/PaceInfo.h" #include "asn1/SecurityInfos.h" #include "CardConnectionWorker.h" +#include "SelectBuilder.h" #include #include @@ -39,16 +40,24 @@ QString CardInfo::getCardTypeString() const switch (mCardType) { case CardType::NONE: + //: ERROR ALL_PLATFORMS No card is present/inserted. The text is only used in DiagnosisView. return tr("not inserted", "Karte"); case CardType::UNKNOWN: + //: ERROR ALL_PLATFORMS An unknown card is present/inserted. The text is only used in DiagnosisView. return tr("unknown type", "Karte"); + case CardType::PASSPORT: + //: ERROR ALL_PLATFORMS A passport card is present/inserted. The text is only used in DiagnosisView. + return tr("Passport"); + case CardType::EID_CARD: + //: ERROR ALL_PLATFORMS An id card is present/inserted. The text is only used in DiagnosisView. return tr("ID card (PA/eAT)"); } Q_UNREACHABLE(); + return QString(); } @@ -64,15 +73,15 @@ bool CardInfo::isEid() const } -QSharedPointer CardInfo::getEfCardAccess() const +bool CardInfo::isPassport() const { - return mEfCardAccess; + return mCardType == CardType::PASSPORT; } -QString CardInfo::getEidApplicationPath() const +QSharedPointer CardInfo::getEfCardAccess() const { - return mCardType == CardType::EID_CARD ? QStringLiteral("e80704007f00070302") : QString(); + return mEfCardAccess; } @@ -102,35 +111,36 @@ bool CardInfo::isPukInoperative() const bool CardInfoFactory::create(const QSharedPointer& pCardConnectionWorker, ReaderInfo& pReaderInfo) { + pReaderInfo.setCardInfo(CardInfo(CardType::UNKNOWN)); + if (pCardConnectionWorker == nullptr) { qCWarning(card) << "No connection to smart card"; - pReaderInfo.setCardInfo(CardInfo(CardType::UNKNOWN)); return false; } - if (!CardInfoFactory::isGermanEidCard(pCardConnectionWorker)) + const CardType type = CardInfoFactory::detectCard(pCardConnectionWorker); + + if (type != CardType::EID_CARD) { qCWarning(card) << "Not a German EID card"; - pReaderInfo.setCardInfo(CardInfo(CardType::UNKNOWN)); return false; } - QSharedPointer efCardAccess = readEfCardAccess(pCardConnectionWorker); - if (efCardAccess == nullptr || !checkEfCardAccess(efCardAccess)) + const auto& efCardAccess = readEfCardAccess(pCardConnectionWorker); + if (!checkEfCardAccess(efCardAccess)) { - qCWarning(card) << "EFCardAccess not found or invalid"; - pReaderInfo.setCardInfo(CardInfo(CardType::UNKNOWN)); + qCWarning(card) << "EFCardAccess not found or is invalid"; return false; } - pReaderInfo.setCardInfo(CardInfo(CardType::EID_CARD, efCardAccess)); + pReaderInfo.setCardInfo(CardInfo(type, efCardAccess)); pCardConnectionWorker->updateRetryCounter(); return true; } -bool CardInfoFactory::isGermanEidCard(const QSharedPointer& pCardConnectionWorker) +CardType CardInfoFactory::detectCard(const QSharedPointer& pCardConnectionWorker) { // This is actually not specified in the CIF, but we do it to make the PersoSim work // 0. Select the master file @@ -151,7 +161,7 @@ bool CardInfoFactory::isGermanEidCard(const QSharedPointer qCDebug(card) << "ResponseApdu return code" << response.getReturnCode(); } - return false; + return CardType::UNKNOWN; } // 1. CL=00, INS=A4=SELECT, P1= 02, P2=0C, Lc=02, Data=2F00 (FI of EF.DIR), Le=absent @@ -160,7 +170,17 @@ bool CardInfoFactory::isGermanEidCard(const QSharedPointer if (returnCode != CardReturnCode::OK || response.getReturnCode() != StatusCode::SUCCESS) { qCWarning(card) << "Cannot select EF.DIR"; - return false; + + qCInfo(card) << "Check for passport..."; + command = SelectBuilder(FileRef::appPassport()).build(); + returnCode = pCardConnectionWorker->transmit(command, response); + if (returnCode != CardReturnCode::OK || response.getReturnCode() != StatusCode::SUCCESS) + { + qCWarning(card) << "Cannot select application identifier"; + return CardType::UNKNOWN; + } + + return CardType::PASSPORT; } // 2. CL=00, INS=B0=Read Binary, P1=00, P2=00 (no offset), Lc=00, Le=5A @@ -169,7 +189,7 @@ bool CardInfoFactory::isGermanEidCard(const QSharedPointer if (returnCode != CardReturnCode::OK || response.getReturnCode() != StatusCode::SUCCESS) { qCWarning(card) << "Cannot read EF.DIR"; - return false; + return CardType::UNKNOWN; } // matching value from CIF @@ -179,10 +199,10 @@ bool CardInfoFactory::isGermanEidCard(const QSharedPointer { qCWarning(card) << "expected EF.DIR(00,5A): " << matchingValue.toHex(); qCWarning(card) << "actual EF.DIR(00,5A): " << response.getData().left(90).toHex(); - return false; + return CardType::UNKNOWN; } - return true; + return CardType::EID_CARD; } @@ -206,6 +226,11 @@ QSharedPointer CardInfoFactory::readEfCardAccess(const QSharedPoin bool CardInfoFactory::checkEfCardAccess(const QSharedPointer& pEfCardAccess) { + if (!pEfCardAccess) + { + return false; + } + /* * At least one PACEInfo must have standardized domain parameters */ diff --git a/src/card/base/CardInfo.h b/src/card/base/CardInfo.h index 97210ad..d5d5ed0 100644 --- a/src/card/base/CardInfo.h +++ b/src/card/base/CardInfo.h @@ -12,7 +12,6 @@ #include #include -class test_CardInfo; namespace governikus { @@ -30,7 +29,6 @@ class CardInfo Q_DECLARE_TR_FUNCTIONS(governikus::CardInfo) private: - friend class ::test_CardInfo; CardType mCardType; QSharedPointer mEfCardAccess; int mRetryCounter; @@ -47,11 +45,10 @@ class CardInfo QString getCardTypeString() const; bool isAvailable() const; bool isEid() const; + bool isPassport() const; QSharedPointer getEfCardAccess() const; - QString getEidApplicationPath() const; - int getRetryCounter() const; bool isRetryCounterDetermined() const; @@ -84,9 +81,9 @@ class CardInfoFactory private: /*! - * Checks, if the smart card is a german eID card, i.e. a NPA or an EAT. + * Checks, if the smart card is a german eID card (i.e. a NPA or an EAT) or a passport. */ - static bool isGermanEidCard(const QSharedPointer& pCardConnectionWorker); + static CardType detectCard(const QSharedPointer& pCardConnectionWorker); /*! * Reads the EF.CardAccess diff --git a/src/card/base/CardOperationResult.h b/src/card/base/CardOperationResult.h deleted file mode 100644 index abd2ee3..0000000 --- a/src/card/base/CardOperationResult.h +++ /dev/null @@ -1,42 +0,0 @@ -/*! - * \brief Generic class representing the result of a card operation, or an error. - * - * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "CardReturnCode.h" - -namespace governikus -{ -template -class CardOperationResult -{ - private: - const CardReturnCode mReturnCode; - const T mPayload; - - public: - CardOperationResult(const CardReturnCode& pReturnCode, const T& pPayload) - : mReturnCode(pReturnCode) - , mPayload(pPayload) - { - } - - - const CardReturnCode& getReturnCode() const - { - return mReturnCode; - } - - - const T& getPayload() const - { - return mPayload; - } - - -}; - -} // namespace governikus diff --git a/src/card/base/CommandApdu.h b/src/card/base/CommandApdu.h index d210bd7..a4cd5e8 100644 --- a/src/card/base/CommandApdu.h +++ b/src/card/base/CommandApdu.h @@ -6,6 +6,8 @@ #include "Apdu.h" +#include + namespace governikus { @@ -25,9 +27,9 @@ class CommandApdu final static const char CLA_COMMAND_CHAINING = 0x10; static const char CLA_SECURE_MESSAGING = 0x0c; - CommandApdu(const QByteArray& pBuffer); - CommandApdu(const QByteArray& pHeader, const QByteArray& pData, int pLe); - CommandApdu(char pCla, char pIns, char pP1, char pP2, const QByteArray& pData = QByteArray(), int pLe = NO_LE); + explicit CommandApdu(const QByteArray& pBuffer); + explicit CommandApdu(const QByteArray& pHeader, const QByteArray& pData, int pLe); + explicit CommandApdu(char pCla, char pIns, char pP1, char pP2, const QByteArray& pData = QByteArray(), int pLe = NO_LE); char getCLA() const; char getINS() const; diff --git a/src/card/base/CommandApduBuilder.cpp b/src/card/base/CommandApduBuilder.cpp new file mode 100644 index 0000000..4718be8 --- /dev/null +++ b/src/card/base/CommandApduBuilder.cpp @@ -0,0 +1,22 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "CommandApduBuilder.h" +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + +/* + * The base class CommandApduBuilder + */ +CommandApduBuilder::CommandApduBuilder() +{ +} + + +CommandApduBuilder::~CommandApduBuilder() +{ +} diff --git a/src/card/base/CommandApduBuilder.h b/src/card/base/CommandApduBuilder.h new file mode 100644 index 0000000..bdbbb79 --- /dev/null +++ b/src/card/base/CommandApduBuilder.h @@ -0,0 +1,25 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApdu.h" + +#include + +namespace governikus +{ + +class CommandApduBuilder +{ + private: + Q_DISABLE_COPY(CommandApduBuilder) + + public: + CommandApduBuilder(); + virtual ~CommandApduBuilder(); + virtual CommandApdu build() = 0; +}; + +} // namespace governikus diff --git a/src/card/base/Commands.cpp b/src/card/base/Commands.cpp deleted file mode 100644 index 02cd8d3..0000000 --- a/src/card/base/Commands.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "Commands.h" - -#include "asn1/ASN1Util.h" -#include "FileRef.h" -#include "SecureMessagingResponse.h" - -#include - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(card) - - -/* - * The base class CommandApduBuilder - */ -CommandApduBuilder::CommandApduBuilder() -{ -} - - -CommandApduBuilder::~CommandApduBuilder() -{ -} - - -/* - * SelectBuilder - */ -SelectBuilder::SelectBuilder(const FileRef& pFileRef) - : CommandApduBuilder() - , mFileRef(pFileRef) -{ -} - - -CommandApdu SelectBuilder::build() -{ - static const char INS = char(0xA4); - return CommandApdu(CommandApdu::CLA, INS, mFileRef.type, static_cast(P2::NONE), mFileRef.path); -} - - -/* - * GetChallengeBuilder - */ - -GetChallengeBuilder::GetChallengeBuilder() -{ -} - - -CommandApdu GetChallengeBuilder::build() -{ - static const char INS = char(0x84); - return CommandApdu(CommandApdu::CLA, INS, 0, 0, QByteArray(), 0x08); -} - - -/* - * GetChallengeResponse - */ - -GetChallengeResponse::GetChallengeResponse() - : ResponseApdu() -{ -} - - -GetChallengeResponse::~GetChallengeResponse() -{ -} - - -QByteArray GetChallengeResponse::getChallenge() const -{ - if (getDataLength() != 8) - { - qCCritical(card) << "Challenge has wrong size. Expect 8 bytes, got " << getDataLength(); - } - return getData(); -} - - -/* - * MSEBuilder - */ -MSEBuilder::MSEBuilder(P1 p1, P2 p2) - : CommandApduBuilder() - , mP1(p1) - , mP2(p2) - , mAuxiliaryData() - , mOid() - , mPublicKey() - , mPrivateKey() - , mEphemeralPublicKey() - , mChat() -{ -} - - -void MSEBuilder::setAuxiliaryData(const QByteArray& pData) -{ - mAuxiliaryData = pData; -} - - -void MSEBuilder::setOid(const QByteArray& pData) -{ - static const char TAG_OID = char(0x80); - mOid = Asn1Util::encode(TAG_OID, pData); -} - - -void MSEBuilder::setPublicKey(const QByteArray& pData) -{ - static const char TAG_PUBLIC_KEY = char(0x83); - mPublicKey = Asn1Util::encode(TAG_PUBLIC_KEY, pData); -} - - -void MSEBuilder::setPublicKey(PacePasswordId pPasswordId) -{ - static const char TAG_PUBLIC_KEY = char(0x83); - QByteArray data; - data += Enum::getValue(pPasswordId); - mPublicKey = Asn1Util::encode(TAG_PUBLIC_KEY, data); -} - - -void MSEBuilder::setPrivateKey(const QByteArray& pData) -{ - static const char TAG_PRIVATE_KEY = char(0x84); - mPrivateKey = Asn1Util::encode(TAG_PRIVATE_KEY, pData); -} - - -void MSEBuilder::setEphemeralPublicKey(const QByteArray& pData) -{ - static const char TAG_EPHEMERAL_PUBLIC_KEY = char(0x91); - mEphemeralPublicKey = Asn1Util::encode(TAG_EPHEMERAL_PUBLIC_KEY, pData); -} - - -void MSEBuilder::setChat(const QByteArray& pData) -{ - mChat = pData; -} - - -CommandApdu MSEBuilder::build() -{ - QByteArray data; - data += mOid; - data += mPublicKey; - data += mPrivateKey; - data += mAuxiliaryData; - data += mEphemeralPublicKey; - data += mChat; - - return CommandApdu(CommandApdu::CLA, static_cast(MSEBuilder::INS::MANAGE_SECURITY_ENVIRONMENT), static_cast(mP1), static_cast(mP2), data); -} - - -/* - * PSOBuilder - */ -PSOBuilder::PSOBuilder(P1 p1, P2 p2) - : CommandApduBuilder() - , mP1(p1) - , mP2(p2) - , mCertificateBody() - , mSignature() -{ -} - - -void PSOBuilder::setCertificateBody(const QByteArray& pData) -{ - mCertificateBody = pData; -} - - -void PSOBuilder::setSignature(const QByteArray& pData) -{ - mSignature = pData; -} - - -CommandApdu PSOBuilder::build() -{ - static const int INS = 0x2a; - - QByteArray data; - data += mCertificateBody; - data += mSignature; - - return CommandApdu(CommandApdu::CLA, INS, char(mP1), char(mP2), data); -} - - -/* - * EABuilder - */ -EABuilder::EABuilder() - : CommandApduBuilder() - , mSignature() -{ -} - - -void EABuilder::setSignature(const QByteArray& pData) -{ - mSignature = pData; -} - - -CommandApdu EABuilder::build() -{ - static const char INS = char(0x82); - return CommandApdu(CommandApdu::CLA, INS, 0, 0, mSignature); -} - - -/* - * GABuilder - */ -GABuilder::GABuilder(char pClassByte) - : CommandApduBuilder() - , mClassByte(pClassByte) - , mCaEphemeralPublicKey() - , mPaceMappingData() - , mPaceEphemeralPublicKey() - , mPaceAuthenticationToken() -{ -} - - -void GABuilder::setCaEphemeralPublicKey(const QByteArray& pData) -{ - static const char TAG_EPHEMERAL_PUBLIC_KEY = char(0x80); - mCaEphemeralPublicKey = Asn1Util::encode(TAG_EPHEMERAL_PUBLIC_KEY, pData); -} - - -void GABuilder::setPaceMappingData(const QByteArray& pData) -{ - static const char TAG_PACE_MAPPING_DATA = char(0x81); - mPaceMappingData = Asn1Util::encode(TAG_PACE_MAPPING_DATA, pData); -} - - -void GABuilder::setPaceEphemeralPublicKey(const QByteArray& pData) -{ - static const char TAG_PACE_EPHEMERAL_PUBLIC_KEY = char(0x83); - mPaceEphemeralPublicKey = Asn1Util::encode(TAG_PACE_EPHEMERAL_PUBLIC_KEY, pData); -} - - -void GABuilder::setPaceAuthenticationToken(const QByteArray& pData) -{ - static const char TAG_PACE_AUTHENTICATION_TOKEN = char(0x85); - mPaceAuthenticationToken = Asn1Util::encode(TAG_PACE_AUTHENTICATION_TOKEN, pData); -} - - -CommandApdu GABuilder::build() -{ - static const char INS = char(0x86); - static const char TAG_DYNAMIC_AUTHENTICATION_DATA = 0x7C; - - QByteArray data; - if (!mCaEphemeralPublicKey.isNull()) - { - data += mCaEphemeralPublicKey; - } - else if (!mPaceMappingData.isNull()) - { - data += mPaceMappingData; - } - else if (!mPaceEphemeralPublicKey.isNull()) - { - data += mPaceEphemeralPublicKey; - } - else if (!mPaceAuthenticationToken.isNull()) - { - data += mPaceAuthenticationToken; - } - data = Asn1Util::encode(TAG_DYNAMIC_AUTHENTICATION_DATA, data); - - return CommandApdu(mClassByte, INS, 0, 0, data, CommandApdu::SHORT_MAX_LE); -} - - -ReadBinaryBuilder::ReadBinaryBuilder(uint pOffset, int pLe) - : CommandApduBuilder() - , mOffset(pOffset) - , mLe(pLe) -{ -} - - -CommandApdu ReadBinaryBuilder::build() -{ - static const char INS = char(0xB0); - return CommandApdu(CommandApdu::CLA, INS, static_cast((mOffset & 0xff00) >> 8), static_cast(mOffset & 0xff), QByteArray(), mLe); -} - - -ResetRetryCounterBuilder::ResetRetryCounterBuilder(const QByteArray& pPin) - : CommandApduBuilder() - , mPin(pPin) -{ -} - - -CommandApdu ResetRetryCounterBuilder::build() -{ - static const char INS = 0x2c; - // P1: 2 (change), 3 (unblock) - const char p1 = mPin.isNull() ? char(3) : char(2); - // P2: 3 (PIN) (2 (CAN) -- not used) - // data: new PIN, when changing - return CommandApdu(CommandApdu::CLA, INS, p1, 3, mPin); -} diff --git a/src/card/base/Commands.h b/src/card/base/Commands.h deleted file mode 100644 index 0b532ee..0000000 --- a/src/card/base/Commands.h +++ /dev/null @@ -1,190 +0,0 @@ -/*! - * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "asn1/Chat.h" -#include "CommandApdu.h" -#include "FileRef.h" -#include "ResponseApdu.h" -#include "SmartCardDefinitions.h" - -class test_Commands; - -namespace governikus -{ - -class CommandApduBuilder -{ - private: - Q_DISABLE_COPY(CommandApduBuilder) - - public: - CommandApduBuilder(); - virtual ~CommandApduBuilder(); - virtual CommandApdu build() = 0; -}; - -class SelectBuilder - : public CommandApduBuilder -{ - private: - const FileRef mFileRef; - - public: - enum class P1 : char - { - SELECT_MF = 0x00, CHILD_DF = 0x01, CHILD_EF = 0x02, PARENT_DF = 0x03, APPLICATION_ID = 0x04, ABS_PATH = 0x08, REL_PATH = 0x09, - }; - - enum class P2 : char - { - FCI = 0x00, FCP = 0x04, FMD = 0x08, NONE = 0x0c, - }; - - SelectBuilder(const FileRef& pFileRef); - CommandApdu build() override; -}; - -class GetChallengeBuilder - : public CommandApduBuilder -{ - public: - GetChallengeBuilder(); - CommandApdu build() override; -}; - -class GetChallengeResponse - : public ResponseApdu -{ - public: - GetChallengeResponse(); - virtual ~GetChallengeResponse(); - QByteArray getChallenge() const; -}; - -class MSEBuilder - : public CommandApduBuilder -{ - public: - enum class INS : char - { - MANAGE_SECURITY_ENVIRONMENT = 0x22, - }; - - enum class P1 : char - { - COMPUTE_DIGITAL_SIGNATURE = 0x41, PUT_HASH = char(0xa0), PERFORM_SECURITY_OPERATION = char(0xc1), SET_DST = char(0x81), ERASE = char(0xF4), - }; - - enum class P2 : char - { - SET_AT = char(0xa4), HASH_ALGORITHM = char(0xaa), COMPUTE_DIGITAL_SIGNATURE = char(0xb6), ENCRYPTION_OPERATION = char(0xb8), DEFAULT_CHANNEL = 0x01, - }; - - MSEBuilder(P1 p1, P2 p2); - void setAuxiliaryData(const QByteArray& pData); - void setOid(const QByteArray& pData); - void setPublicKey(const QByteArray& pData); - void setPublicKey(PacePasswordId pPassword); - void setPrivateKey(const QByteArray& pData); - void setEphemeralPublicKey(const QByteArray& pData); - void setChat(const QByteArray& pData); - CommandApdu build() override; - - private: - P1 mP1; - P2 mP2; - QByteArray mAuxiliaryData; - QByteArray mOid; - QByteArray mPublicKey; - QByteArray mPrivateKey; - QByteArray mEphemeralPublicKey; - QByteArray mChat; -}; - -class PSOBuilder - : public CommandApduBuilder -{ - public: - enum class P1 : int - { - DECRYPT = 0x80, ENCRYPT = 0x86, SIGN_HASH = 0x9e, VERIFY = 0x00, - }; - - enum class P2 : int - { - UNCRYPTED_DATA = 0x80, ENCRYPTED_DATA = 0x86, HASH_VALUE = 0x9a, CERTIFICATE = 0xbe, - }; - - PSOBuilder(P1 p1, P2 p2); - void setCertificateBody(const QByteArray& pData); - void setSignature(const QByteArray& pData); - CommandApdu build() override; - - private: - friend class ::test_Commands; - P1 mP1; - P2 mP2; - QByteArray mCertificateBody; - QByteArray mSignature; -}; - -class EABuilder - : public CommandApduBuilder -{ - public: - EABuilder(); - void setSignature(const QByteArray& pData); - CommandApdu build() override; - - private: - friend class ::test_Commands; - QByteArray mSignature; -}; - -class GABuilder - : public CommandApduBuilder -{ - public: - GABuilder(char pClassByte = CommandApdu::CLA); - void setCaEphemeralPublicKey(const QByteArray& pData); - void setPaceMappingData(const QByteArray& pData); - void setPaceEphemeralPublicKey(const QByteArray& pData); - void setPaceAuthenticationToken(const QByteArray& pData); - CommandApdu build() override; - - private: - friend class ::test_Commands; - char mClassByte; - QByteArray mCaEphemeralPublicKey; - QByteArray mPaceMappingData; - QByteArray mPaceEphemeralPublicKey; - QByteArray mPaceAuthenticationToken; -}; - -class ReadBinaryBuilder - : public CommandApduBuilder -{ - private: - uint mOffset; - int mLe; - - public: - ReadBinaryBuilder(uint pOffset, int pLe); - CommandApdu build() override; -}; - -class ResetRetryCounterBuilder - : public CommandApduBuilder -{ - public: - ResetRetryCounterBuilder(const QByteArray& pPin = QByteArray()); - CommandApdu build() override; - - private: - QByteArray mPin; -}; - -} // namespace governikus diff --git a/src/card/base/EABuilder.cpp b/src/card/base/EABuilder.cpp new file mode 100644 index 0000000..34c53ee --- /dev/null +++ b/src/card/base/EABuilder.cpp @@ -0,0 +1,33 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "EABuilder.h" + +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + +/* + * EABuilder + */ +EABuilder::EABuilder() + : CommandApduBuilder() + , mSignature() +{ +} + + +void EABuilder::setSignature(const QByteArray& pData) +{ + mSignature = pData; +} + + +CommandApdu EABuilder::build() +{ + static const char INS = char(0x82); + return CommandApdu(CommandApdu::CLA, INS, 0, 0, mSignature); +} diff --git a/src/card/base/EABuilder.h b/src/card/base/EABuilder.h new file mode 100644 index 0000000..d48b181 --- /dev/null +++ b/src/card/base/EABuilder.h @@ -0,0 +1,27 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApduBuilder.h" + +class test_Commands; + +namespace governikus +{ + +class EABuilder + : public CommandApduBuilder +{ + public: + EABuilder(); + void setSignature(const QByteArray& pData); + CommandApdu build() override; + + private: + friend class ::test_Commands; + QByteArray mSignature; +}; + +} // namespace governikus diff --git a/src/card/base/EstablishPaceChannel.cpp b/src/card/base/EstablishPaceChannel.cpp index 16a8ebc..b9e7276 100644 --- a/src/card/base/EstablishPaceChannel.cpp +++ b/src/card/base/EstablishPaceChannel.cpp @@ -12,30 +12,10 @@ #include #include - using namespace governikus; - Q_DECLARE_LOGGING_CATEGORY(card) - -namespace -{ -template QByteArray readByteArray(const QByteArray& pInput, int& pOffset) -{ - Q_ASSERT(sizeof(T) < INT_MAX); - - T length = qFromLittleEndian(pInput.data() + pOffset); - pOffset += static_cast(sizeof(T)); - QByteArray result = pInput.mid(pOffset, length); - pOffset += length; - return result; -} - - -} // namespace - - namespace governikus { @@ -147,7 +127,7 @@ CommandApdu EstablishPaceChannel::createCommandDataCcid() } if (!mCertificateDescription.isEmpty()) { - const uchar* unsignedCharPointer = reinterpret_cast(mCertificateDescription.constData()); + const auto* unsignedCharPointer = reinterpret_cast(mCertificateDescription.constData()); decodeAsn1Object(&channelInput->mCertificateDescription, &unsignedCharPointer, mCertificateDescription.size()); } diff --git a/src/card/base/EstablishPaceChannelOutput.cpp b/src/card/base/EstablishPaceChannelOutput.cpp index bc2deee..279a844 100644 --- a/src/card/base/EstablishPaceChannelOutput.cpp +++ b/src/card/base/EstablishPaceChannelOutput.cpp @@ -11,13 +11,11 @@ #include #include +Q_DECLARE_LOGGING_CATEGORY(card) using namespace governikus; -Q_DECLARE_LOGGING_CATEGORY(card) - - namespace { template QByteArray readByteArray(const QByteArray& pInput, int& pOffset) @@ -55,8 +53,8 @@ IMPLEMENT_ASN1_OBJECT(ESTABLISHPACECHANNELOUTPUT) } // namespace governikus -EstablishPaceChannelOutput::EstablishPaceChannelOutput() - : mPaceReturnCode(CardReturnCode::UNKNOWN) +EstablishPaceChannelOutput::EstablishPaceChannelOutput(CardReturnCode pPaceReturnCode) + : mPaceReturnCode(pPaceReturnCode) , mEfCardAccess() , mCarCurr() , mCarPrev() @@ -149,10 +147,10 @@ void EstablishPaceChannelOutput::parse(const QByteArray& pControlOutput, PacePas qCWarning(card) << "Output of EstablishPaceChannel has wrong size"; return; } - quint32 paceReturnCode = qFromLittleEndian(pControlOutput.data()); + auto paceReturnCode = qFromLittleEndian(pControlOutput.data()); mPaceReturnCode = parseReturnCode(paceReturnCode, pPasswordId); - quint16 dataLength = qFromLittleEndian(pControlOutput.data() + 4); + auto dataLength = qFromLittleEndian(pControlOutput.data() + 4); if (pControlOutput.size() < 6 + dataLength) { qCWarning(card) << "Output of EstablishPaceChannel has wrong size"; @@ -165,7 +163,7 @@ void EstablishPaceChannelOutput::parse(const QByteArray& pControlOutput, PacePas } // Response data according to PC/SC Part 10 amendment 1.1 - quint16 status = qFromBigEndian(pControlOutput.data() + 6); + auto status = qFromBigEndian(pControlOutput.data() + 6); if (status != StatusCode::SUCCESS) { qCWarning(card) << "PACE failed. Status code:" << status; @@ -210,7 +208,7 @@ QByteArray EstablishPaceChannelOutput::toCcid() const Asn1OctetStringUtil::setValue(mStatusMseSetAt, establishPaceChannelOutput->mStatusMSESetAt); } - const uchar* unsignedCharPointer = reinterpret_cast(mEfCardAccess.constData()); + const auto* unsignedCharPointer = reinterpret_cast(mEfCardAccess.constData()); decodeAsn1Object(&establishPaceChannelOutput->mEfCardAccess, &unsignedCharPointer, mEfCardAccess.size()); establishPaceChannelOutput->mIdPICC = ASN1_OCTET_STRING_new(); @@ -274,36 +272,36 @@ void EstablishPaceChannelOutput::parseFromCcid(const QByteArray& pOutput, PacePa quint32 paceReturnCode; QDataStream(paceReturnCodeBytes) >> paceReturnCode; mPaceReturnCode = parseReturnCode(paceReturnCode, pPasswordId); - qDebug() << "mPaceReturnCode:" << mPaceReturnCode << paceReturnCodeBytes.toHex(); + qCDebug(card) << "mPaceReturnCode:" << mPaceReturnCode << paceReturnCodeBytes.toHex(); if (channelOutput->mStatusMSESetAt) { mStatusMseSetAt = Asn1OctetStringUtil::getValue(channelOutput->mStatusMSESetAt); - qDebug() << "mStatusMseSetAt:" << mStatusMseSetAt.toHex(); + qCDebug(card) << "mStatusMseSetAt:" << mStatusMseSetAt.toHex(); } if (channelOutput->mEfCardAccess) { mEfCardAccess = encodeObject(channelOutput->mEfCardAccess); - qDebug() << "mEfCardAccess:" << mEfCardAccess.toHex(); + qCDebug(card) << "mEfCardAccess:" << mEfCardAccess.toHex(); } if (channelOutput->mIdPICC != nullptr) { mIdIcc = Asn1OctetStringUtil::getValue(channelOutput->mIdPICC); - qDebug() << "mIdIcc:" << mIdIcc.toHex(); + qCDebug(card) << "mIdIcc:" << mIdIcc.toHex(); } if (channelOutput->mCurCAR != nullptr) { mCarCurr = Asn1OctetStringUtil::getValue(channelOutput->mCurCAR); - qDebug() << "mCarCurr:" << mCarCurr; + qCDebug(card) << "mCarCurr:" << mCarCurr; } if (channelOutput->mPrevCAR != nullptr) { mCarPrev = Asn1OctetStringUtil::getValue(channelOutput->mPrevCAR); - qDebug() << "mCarPrev:" << mCarPrev; + qCDebug(card) << "mCarPrev:" << mCarPrev; } } diff --git a/src/card/base/EstablishPaceChannelOutput.h b/src/card/base/EstablishPaceChannelOutput.h index 334acdc..ddfcc3f 100644 --- a/src/card/base/EstablishPaceChannelOutput.h +++ b/src/card/base/EstablishPaceChannelOutput.h @@ -55,7 +55,7 @@ class EstablishPaceChannelOutput QByteArray mStatusMseSetAt; public: - EstablishPaceChannelOutput(); + EstablishPaceChannelOutput(CardReturnCode pPaceReturnCode = CardReturnCode::UNKNOWN); /** * Defined in pcsc10_v2.02.08_amd1.1 diff --git a/src/card/base/FileRef.cpp b/src/card/base/FileRef.cpp index 1a462ee..bbabcb4 100644 --- a/src/card/base/FileRef.cpp +++ b/src/card/base/FileRef.cpp @@ -2,9 +2,10 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ -#include "Commands.h" #include "FileRef.h" +#include "SelectBuilder.h" + using namespace governikus; FileRef FileRef::efDir() @@ -43,6 +44,18 @@ FileRef FileRef::appESign() } +FileRef FileRef::appEId() +{ + return FileRef(static_cast(SelectBuilder::P1::APPLICATION_ID), QByteArray::fromHex("e80704007f00070302")); +} + + +FileRef FileRef::appPassport() +{ + return FileRef(static_cast(SelectBuilder::P1::APPLICATION_ID), QByteArray::fromHex("a0000002471001")); +} + + FileRef::FileRef(char pType, const QByteArray& pPath) : type(pType) , path(pPath) diff --git a/src/card/base/FileRef.h b/src/card/base/FileRef.h index 7105036..f9b2eb5 100644 --- a/src/card/base/FileRef.h +++ b/src/card/base/FileRef.h @@ -23,6 +23,8 @@ struct FileRef static FileRef efCardAccess(); static FileRef efCardSecurity(); static FileRef appESign(); + static FileRef appEId(); + static FileRef appPassport(); }; diff --git a/src/card/base/GABuilder.cpp b/src/card/base/GABuilder.cpp new file mode 100644 index 0000000..8b24bc2 --- /dev/null +++ b/src/card/base/GABuilder.cpp @@ -0,0 +1,83 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "GABuilder.h" + +#include "asn1/ASN1Util.h" + +#include + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(card) + +/* + * GABuilder + */ +GABuilder::GABuilder(char pClassByte) + : CommandApduBuilder() + , mClassByte(pClassByte) + , mCaEphemeralPublicKey() + , mPaceMappingData() + , mPaceEphemeralPublicKey() + , mPaceAuthenticationToken() +{ +} + + +void GABuilder::setCaEphemeralPublicKey(const QByteArray& pData) +{ + static const char TAG_EPHEMERAL_PUBLIC_KEY = char(0x80); + mCaEphemeralPublicKey = Asn1Util::encode(TAG_EPHEMERAL_PUBLIC_KEY, pData); +} + + +void GABuilder::setPaceMappingData(const QByteArray& pData) +{ + static const char TAG_PACE_MAPPING_DATA = char(0x81); + mPaceMappingData = Asn1Util::encode(TAG_PACE_MAPPING_DATA, pData); +} + + +void GABuilder::setPaceEphemeralPublicKey(const QByteArray& pData) +{ + static const char TAG_PACE_EPHEMERAL_PUBLIC_KEY = char(0x83); + mPaceEphemeralPublicKey = Asn1Util::encode(TAG_PACE_EPHEMERAL_PUBLIC_KEY, pData); +} + + +void GABuilder::setPaceAuthenticationToken(const QByteArray& pData) +{ + static const char TAG_PACE_AUTHENTICATION_TOKEN = char(0x85); + mPaceAuthenticationToken = Asn1Util::encode(TAG_PACE_AUTHENTICATION_TOKEN, pData); +} + + +CommandApdu GABuilder::build() +{ + static const char INS = char(0x86); + static const char TAG_DYNAMIC_AUTHENTICATION_DATA = 0x7C; + + QByteArray data; + if (!mCaEphemeralPublicKey.isNull()) + { + data += mCaEphemeralPublicKey; + } + else if (!mPaceMappingData.isNull()) + { + data += mPaceMappingData; + } + else if (!mPaceEphemeralPublicKey.isNull()) + { + data += mPaceEphemeralPublicKey; + } + else if (!mPaceAuthenticationToken.isNull()) + { + data += mPaceAuthenticationToken; + } + data = Asn1Util::encode(TAG_DYNAMIC_AUTHENTICATION_DATA, data); + + return CommandApdu(mClassByte, INS, 0, 0, data, CommandApdu::SHORT_MAX_LE); +} diff --git a/src/card/base/GABuilder.h b/src/card/base/GABuilder.h new file mode 100644 index 0000000..55f22e4 --- /dev/null +++ b/src/card/base/GABuilder.h @@ -0,0 +1,34 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApduBuilder.h" + +class test_Commands; + +namespace governikus +{ + +class GABuilder + : public CommandApduBuilder +{ + public: + explicit GABuilder(char pClassByte = CommandApdu::CLA); + void setCaEphemeralPublicKey(const QByteArray& pData); + void setPaceMappingData(const QByteArray& pData); + void setPaceEphemeralPublicKey(const QByteArray& pData); + void setPaceAuthenticationToken(const QByteArray& pData); + CommandApdu build() override; + + private: + friend class ::test_Commands; + char mClassByte; + QByteArray mCaEphemeralPublicKey; + QByteArray mPaceMappingData; + QByteArray mPaceEphemeralPublicKey; + QByteArray mPaceAuthenticationToken; +}; + +} // namespace governikus diff --git a/src/card/base/GeneralAuthenticateResponse.cpp b/src/card/base/GeneralAuthenticateResponse.cpp index 7216f12..5e82d94 100644 --- a/src/card/base/GeneralAuthenticateResponse.cpp +++ b/src/card/base/GeneralAuthenticateResponse.cpp @@ -7,31 +7,30 @@ #include +Q_DECLARE_LOGGING_CATEGORY(card) using namespace governikus; -Q_DECLARE_LOGGING_CATEGORY(card) - - -GAResponseApdu::GAResponseApdu() - : ResponseApdu() +GAResponseApdu::GAResponseApdu(const ResponseApdu& pResponseApdu) + : mResponseApdu(pResponseApdu) { + if (!isValid()) + { + qCCritical(card) << "General authentication data seems invalid because of StatusCode:" << pResponseApdu.getReturnCode(); + } } -void GAResponseApdu::setBuffer(const QByteArray& pBuffer) +bool GAResponseApdu::isValid() const { - ResponseApdu::setBuffer(pBuffer); + return StatusCode::SUCCESS == getReturnCode(); +} - StatusCode statusCode = getReturnCode(); - if (statusCode != StatusCode::SUCCESS) - { - qCCritical(card) << "Avoid parsing of the general authentication data because of StatusCode" << statusCode; - return; - } - parseDynamicAuthenticationData(getData()); +StatusCode GAResponseApdu::getReturnCode() const +{ + return mResponseApdu.getReturnCode(); } @@ -67,14 +66,18 @@ void GAEncryptedNonceResponse::parseDynamicAuthenticationData(const QByteArray& } -GAEncryptedNonceResponse::GAEncryptedNonceResponse() - : GAResponseApdu() +GAEncryptedNonceResponse::GAEncryptedNonceResponse(const ResponseApdu& pResponseApdu) + : GAResponseApdu(pResponseApdu) , mEncryptedNonce() { + if (isValid()) + { + parseDynamicAuthenticationData(mResponseApdu.getData()); + } } -const QByteArray& GAEncryptedNonceResponse::getEncryptedNonce() +const QByteArray& GAEncryptedNonceResponse::getEncryptedNonce() const { return mEncryptedNonce; } @@ -112,14 +115,18 @@ void GAMapNonceResponse::parseDynamicAuthenticationData(const QByteArray& pDynam } -GAMapNonceResponse::GAMapNonceResponse() - : GAResponseApdu() +GAMapNonceResponse::GAMapNonceResponse(const ResponseApdu& pResponseApdu) + : GAResponseApdu(pResponseApdu) , mMappingData() { + if (isValid()) + { + parseDynamicAuthenticationData(mResponseApdu.getData()); + } } -const QByteArray& GAMapNonceResponse::getMappingData() +const QByteArray& GAMapNonceResponse::getMappingData() const { return mMappingData; } @@ -157,14 +164,18 @@ void GAPerformKeyAgreementResponse::parseDynamicAuthenticationData(const QByteAr } -GAPerformKeyAgreementResponse::GAPerformKeyAgreementResponse() - : GAResponseApdu() +GAPerformKeyAgreementResponse::GAPerformKeyAgreementResponse(const ResponseApdu& pResponseApdu) + : GAResponseApdu(pResponseApdu) , mEphemeralPublicKey() { + if (isValid()) + { + parseDynamicAuthenticationData(mResponseApdu.getData()); + } } -const QByteArray& GAPerformKeyAgreementResponse::getEphemeralPublicKey() +const QByteArray& GAPerformKeyAgreementResponse::getEphemeralPublicKey() const { return mEphemeralPublicKey; } @@ -204,33 +215,37 @@ void GAMutualAuthenticationResponse::parseDynamicAuthenticationData(const QByteA mCarCurr = Asn1OctetStringUtil::getValue(data->mCarCurr); mCarPrev = Asn1OctetStringUtil::getValue(data->mCarPrev); - qDebug() << "mCarCurr" << mCarCurr; - qDebug() << "mCarPrev" << mCarPrev; + qCDebug(card) << "mCarCurr" << mCarCurr; + qCDebug(card) << "mCarPrev" << mCarPrev; } -GAMutualAuthenticationResponse::GAMutualAuthenticationResponse() - : GAResponseApdu() +GAMutualAuthenticationResponse::GAMutualAuthenticationResponse(const ResponseApdu& pResponseApdu) + : GAResponseApdu(pResponseApdu) , mAuthenticationToken() , mCarCurr() , mCarPrev() { + if (isValid()) + { + parseDynamicAuthenticationData(mResponseApdu.getData()); + } } -const QByteArray& GAMutualAuthenticationResponse::getAuthenticationToken() +const QByteArray& GAMutualAuthenticationResponse::getAuthenticationToken() const { return mAuthenticationToken; } -const QByteArray& GAMutualAuthenticationResponse::getCarCurr() +const QByteArray& GAMutualAuthenticationResponse::getCarCurr() const { return mCarCurr; } -const QByteArray& GAMutualAuthenticationResponse::getCarPrev() +const QByteArray& GAMutualAuthenticationResponse::getCarPrev() const { return mCarPrev; } @@ -270,21 +285,25 @@ void GAChipAuthenticationResponse::parseDynamicAuthenticationData(const QByteArr } -GAChipAuthenticationResponse::GAChipAuthenticationResponse() - : GAResponseApdu() +GAChipAuthenticationResponse::GAChipAuthenticationResponse(const ResponseApdu& pResponseApdu) + : GAResponseApdu(pResponseApdu) , mNonce() , mAuthenticationToken() { + if (isValid()) + { + parseDynamicAuthenticationData(mResponseApdu.getData()); + } } -const QByteArray& GAChipAuthenticationResponse::getNonce() +const QByteArray& GAChipAuthenticationResponse::getNonce() const { return mNonce; } -const QByteArray& GAChipAuthenticationResponse::getAuthenticationToken() +const QByteArray& GAChipAuthenticationResponse::getAuthenticationToken() const { return mAuthenticationToken; } diff --git a/src/card/base/GeneralAuthenticateResponse.h b/src/card/base/GeneralAuthenticateResponse.h index 1149ae2..80c5481 100644 --- a/src/card/base/GeneralAuthenticateResponse.h +++ b/src/card/base/GeneralAuthenticateResponse.h @@ -14,16 +14,15 @@ namespace governikus { class GAResponseApdu - : public ResponseApdu { - private: - virtual void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData) = 0; + protected: + ResponseApdu mResponseApdu; + + bool isValid() const; public: - GAResponseApdu(); - virtual ~GAResponseApdu() override = default; - virtual void setBuffer(const QByteArray& pBuffer) override; - + explicit GAResponseApdu(const ResponseApdu& pResponseApdu); + StatusCode getReturnCode() const; }; @@ -33,10 +32,10 @@ class GAResponseApdu * * EncryptedNonce ::= APPLICATION [0x00] IMPLICIT OCTET_STRING */ -typedef struct ga_encryptednoncedata_st +using GA_ENCRYPTEDNONCEDATA = struct ga_encryptednoncedata_st { ASN1_OCTET_STRING* mEncryptedNonce; -} GA_ENCRYPTEDNONCEDATA; +}; DECLARE_ASN1_OBJECT(GA_ENCRYPTEDNONCEDATA) @@ -44,14 +43,12 @@ class GAEncryptedNonceResponse : public GAResponseApdu { private: - virtual void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData) override; + void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData); QByteArray mEncryptedNonce; public: - GAEncryptedNonceResponse(); - virtual ~GAEncryptedNonceResponse() override = default; - const QByteArray& getEncryptedNonce(); - + explicit GAEncryptedNonceResponse(const ResponseApdu& pResponseApdu); + const QByteArray& getEncryptedNonce() const; }; @@ -61,10 +58,10 @@ class GAEncryptedNonceResponse * * MappingData ::= APPLICATION [0x02] IMPLICIT OCTET_STRING */ -typedef struct ga_mapnoncedata_st +using GA_MAPNONCEDATA = struct ga_mapnoncedata_st { ASN1_OCTET_STRING* mMappingData; -} GA_MAPNONCEDATA; +}; DECLARE_ASN1_OBJECT(GA_MAPNONCEDATA) @@ -72,14 +69,12 @@ class GAMapNonceResponse : public GAResponseApdu { private: - virtual void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData) override; + void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData); QByteArray mMappingData; public: - GAMapNonceResponse(); - virtual ~GAMapNonceResponse() override = default; - const QByteArray& getMappingData(); - + explicit GAMapNonceResponse(const ResponseApdu& pResponseApdu); + const QByteArray& getMappingData() const; }; @@ -89,10 +84,10 @@ class GAMapNonceResponse * * EphemeralPublicKey ::= APPLICATION [0x04] IMPLICIT OCTET_STRING */ -typedef struct ga_performkeyagreementdata_st +using GA_PERFORMKEYAGREEMENTDATA = struct ga_performkeyagreementdata_st { ASN1_OCTET_STRING* mEphemeralPublicKey; -} GA_PERFORMKEYAGREEMENTDATA; +}; DECLARE_ASN1_OBJECT(GA_PERFORMKEYAGREEMENTDATA) @@ -100,14 +95,12 @@ class GAPerformKeyAgreementResponse : public GAResponseApdu { private: - virtual void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData) override; + void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData); QByteArray mEphemeralPublicKey; public: - GAPerformKeyAgreementResponse(); - virtual ~GAPerformKeyAgreementResponse() override = default; - const QByteArray& getEphemeralPublicKey(); - + explicit GAPerformKeyAgreementResponse(const ResponseApdu& pResponseApdu); + const QByteArray& getEphemeralPublicKey() const; }; @@ -119,12 +112,12 @@ class GAPerformKeyAgreementResponse * CarCurr ::= APPLICATION [0x00] IMPLICIT OCTET_STRING OPTIONAL * CarPrev ::= APPLICATION [0x00] IMPLICIT OCTET_STRING OPTIONAL */ -typedef struct ga_mutualauthenticationdata_st +using GA_MUTUALAUTHENTICATIONDATA = struct ga_mutualauthenticationdata_st { ASN1_OCTET_STRING* mAuthenticationToken; ASN1_OCTET_STRING* mCarCurr; ASN1_OCTET_STRING* mCarPrev; -} GA_MUTUALAUTHENTICATIONDATA; +}; DECLARE_ASN1_OBJECT(GA_MUTUALAUTHENTICATIONDATA) @@ -132,16 +125,14 @@ class GAMutualAuthenticationResponse : public GAResponseApdu { private: - virtual void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData) override; + void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData); QByteArray mAuthenticationToken, mCarCurr, mCarPrev; public: - GAMutualAuthenticationResponse(); - virtual ~GAMutualAuthenticationResponse() override = default; - const QByteArray& getAuthenticationToken(); - const QByteArray& getCarCurr(); - const QByteArray& getCarPrev(); - + explicit GAMutualAuthenticationResponse(const ResponseApdu& pResponseApdu); + const QByteArray& getAuthenticationToken() const; + const QByteArray& getCarCurr() const; + const QByteArray& getCarPrev() const; }; @@ -152,11 +143,11 @@ class GAMutualAuthenticationResponse * Nonce ::= APPLICATION [0x01] IMPLICIT OCTET_STRING * AuthenticationToken ::= APPLICATION [0x02] IMPLICIT OCTET_STRING */ -typedef struct ga_chipauthenticationdata_st +using GA_CHIPAUTHENTICATIONDATA = struct ga_chipauthenticationdata_st { ASN1_OCTET_STRING* mNonce; ASN1_OCTET_STRING* mAuthenticationToken; -} GA_CHIPAUTHENTICATIONDATA; +}; DECLARE_ASN1_OBJECT(GA_CHIPAUTHENTICATIONDATA) @@ -164,15 +155,13 @@ class GAChipAuthenticationResponse : public GAResponseApdu { private: - virtual void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData) override; + void parseDynamicAuthenticationData(const QByteArray& pDynamicAuthenticationData); QByteArray mNonce, mAuthenticationToken; public: - GAChipAuthenticationResponse(); - virtual ~GAChipAuthenticationResponse() override = default; - const QByteArray& getNonce(); - const QByteArray& getAuthenticationToken(); - + explicit GAChipAuthenticationResponse(const ResponseApdu& pResponseApdu); + const QByteArray& getNonce() const; + const QByteArray& getAuthenticationToken() const; }; diff --git a/src/card/base/GetChallengeBuilder.cpp b/src/card/base/GetChallengeBuilder.cpp new file mode 100644 index 0000000..5730430 --- /dev/null +++ b/src/card/base/GetChallengeBuilder.cpp @@ -0,0 +1,26 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "GetChallengeBuilder.h" + +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + +/* + * GetChallengeBuilder + */ + +GetChallengeBuilder::GetChallengeBuilder() +{ +} + + +CommandApdu GetChallengeBuilder::build() +{ + static const char INS = char(0x84); + return CommandApdu(CommandApdu::CLA, INS, 0, 0, QByteArray(), 0x08); +} diff --git a/src/card/base/GetChallengeBuilder.h b/src/card/base/GetChallengeBuilder.h new file mode 100644 index 0000000..97db22b --- /dev/null +++ b/src/card/base/GetChallengeBuilder.h @@ -0,0 +1,20 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApduBuilder.h" + +namespace governikus +{ + +class GetChallengeBuilder + : public CommandApduBuilder +{ + public: + GetChallengeBuilder(); + CommandApdu build() override; +}; + +} // namespace governikus diff --git a/src/card/base/InputAPDUInfo.cpp b/src/card/base/InputAPDUInfo.cpp index e77de9a..35120e1 100644 --- a/src/card/base/InputAPDUInfo.cpp +++ b/src/card/base/InputAPDUInfo.cpp @@ -6,13 +6,6 @@ using namespace governikus; -InputAPDUInfo::InputAPDUInfo() - : mInputApdu() - , mAcceptableStatusCodes() -{ -} - - InputAPDUInfo::InputAPDUInfo(const QByteArray& pInputApdu) : mInputApdu(pInputApdu) , mAcceptableStatusCodes() diff --git a/src/card/base/InputAPDUInfo.h b/src/card/base/InputAPDUInfo.h index f34a5d5..ee2e1d7 100644 --- a/src/card/base/InputAPDUInfo.h +++ b/src/card/base/InputAPDUInfo.h @@ -4,12 +4,11 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ +#pragma once + #include "CommandApdu.h" #include -#include - -#pragma once namespace governikus { @@ -17,8 +16,7 @@ namespace governikus class InputAPDUInfo { public: - InputAPDUInfo(); - InputAPDUInfo(const QByteArray& pInputApdu); + explicit InputAPDUInfo(const QByteArray& pInputApdu = QByteArray()); bool isValid() const diff --git a/src/card/base/MSEBuilder.cpp b/src/card/base/MSEBuilder.cpp new file mode 100644 index 0000000..9c834dd --- /dev/null +++ b/src/card/base/MSEBuilder.cpp @@ -0,0 +1,92 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "MSEBuilder.h" + +#include "asn1/ASN1Util.h" + +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + +/* + * MSEBuilder + */ +MSEBuilder::MSEBuilder(P1 p1, P2 p2) + : CommandApduBuilder() + , mP1(p1) + , mP2(p2) + , mAuxiliaryData() + , mOid() + , mPublicKey() + , mPrivateKey() + , mEphemeralPublicKey() + , mChat() +{ +} + + +void MSEBuilder::setAuxiliaryData(const QByteArray& pData) +{ + mAuxiliaryData = pData; +} + + +void MSEBuilder::setOid(const QByteArray& pData) +{ + static const char TAG_OID = char(0x80); + mOid = Asn1Util::encode(TAG_OID, pData); +} + + +void MSEBuilder::setPublicKey(const QByteArray& pData) +{ + static const char TAG_PUBLIC_KEY = char(0x83); + mPublicKey = Asn1Util::encode(TAG_PUBLIC_KEY, pData); +} + + +void MSEBuilder::setPublicKey(PacePasswordId pPasswordId) +{ + static const char TAG_PUBLIC_KEY = char(0x83); + QByteArray data; + data += Enum::getValue(pPasswordId); + mPublicKey = Asn1Util::encode(TAG_PUBLIC_KEY, data); +} + + +void MSEBuilder::setPrivateKey(const QByteArray& pData) +{ + static const char TAG_PRIVATE_KEY = char(0x84); + mPrivateKey = Asn1Util::encode(TAG_PRIVATE_KEY, pData); +} + + +void MSEBuilder::setEphemeralPublicKey(const QByteArray& pData) +{ + static const char TAG_EPHEMERAL_PUBLIC_KEY = char(0x91); + mEphemeralPublicKey = Asn1Util::encode(TAG_EPHEMERAL_PUBLIC_KEY, pData); +} + + +void MSEBuilder::setChat(const QByteArray& pData) +{ + mChat = pData; +} + + +CommandApdu MSEBuilder::build() +{ + QByteArray data; + data += mOid; + data += mPublicKey; + data += mPrivateKey; + data += mAuxiliaryData; + data += mEphemeralPublicKey; + data += mChat; + + return CommandApdu(CommandApdu::CLA, static_cast(MSEBuilder::INS::MANAGE_SECURITY_ENVIRONMENT), static_cast(mP1), static_cast(mP2), data); +} diff --git a/src/card/base/MSEBuilder.h b/src/card/base/MSEBuilder.h new file mode 100644 index 0000000..7eedcf7 --- /dev/null +++ b/src/card/base/MSEBuilder.h @@ -0,0 +1,55 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApduBuilder.h" +#include "SmartCardDefinitions.h" + +class test_Commands; + +namespace governikus +{ + +class MSEBuilder + : public CommandApduBuilder +{ + public: + enum class INS : char + { + MANAGE_SECURITY_ENVIRONMENT = 0x22, + }; + + enum class P1 : char + { + COMPUTE_DIGITAL_SIGNATURE = 0x41, PUT_HASH = char(0xa0), PERFORM_SECURITY_OPERATION = char(0xc1), SET_DST = char(0x81), ERASE = char(0xF4), + }; + + enum class P2 : char + { + SET_AT = char(0xa4), HASH_ALGORITHM = char(0xaa), COMPUTE_DIGITAL_SIGNATURE = char(0xb6), ENCRYPTION_OPERATION = char(0xb8), DEFAULT_CHANNEL = 0x01, + }; + + explicit MSEBuilder(P1 p1, P2 p2); + void setAuxiliaryData(const QByteArray& pData); + void setOid(const QByteArray& pData); + void setPublicKey(const QByteArray& pData); + void setPublicKey(PacePasswordId pPassword); + void setPrivateKey(const QByteArray& pData); + void setEphemeralPublicKey(const QByteArray& pData); + void setChat(const QByteArray& pData); + CommandApdu build() override; + + private: + P1 mP1; + P2 mP2; + QByteArray mAuxiliaryData; + QByteArray mOid; + QByteArray mPublicKey; + QByteArray mPrivateKey; + QByteArray mEphemeralPublicKey; + QByteArray mChat; +}; + +} // namespace governikus diff --git a/src/card/base/PSOBuilder.cpp b/src/card/base/PSOBuilder.cpp new file mode 100644 index 0000000..b6d6db5 --- /dev/null +++ b/src/card/base/PSOBuilder.cpp @@ -0,0 +1,47 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "PSOBuilder.h" + +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + +/* + * PSOBuilder + */ +PSOBuilder::PSOBuilder(P1 p1, P2 p2) + : CommandApduBuilder() + , mP1(p1) + , mP2(p2) + , mCertificateBody() + , mSignature() +{ +} + + +void PSOBuilder::setCertificateBody(const QByteArray& pData) +{ + mCertificateBody = pData; +} + + +void PSOBuilder::setSignature(const QByteArray& pData) +{ + mSignature = pData; +} + + +CommandApdu PSOBuilder::build() +{ + static const int INS = 0x2a; + + QByteArray data; + data += mCertificateBody; + data += mSignature; + + return CommandApdu(CommandApdu::CLA, INS, char(mP1), char(mP2), data); +} diff --git a/src/card/base/PSOBuilder.h b/src/card/base/PSOBuilder.h new file mode 100644 index 0000000..fc628fa --- /dev/null +++ b/src/card/base/PSOBuilder.h @@ -0,0 +1,41 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApduBuilder.h" + +class test_Commands; + +namespace governikus +{ + +class PSOBuilder + : public CommandApduBuilder +{ + public: + enum class P1 : int + { + DECRYPT = 0x80, ENCRYPT = 0x86, SIGN_HASH = 0x9e, VERIFY = 0x00, + }; + + enum class P2 : int + { + UNCRYPTED_DATA = 0x80, ENCRYPTED_DATA = 0x86, HASH_VALUE = 0x9a, CERTIFICATE = 0xbe, + }; + + explicit PSOBuilder(P1 p1, P2 p2); + void setCertificateBody(const QByteArray& pData); + void setSignature(const QByteArray& pData); + CommandApdu build() override; + + private: + friend class ::test_Commands; + P1 mP1; + P2 mP2; + QByteArray mCertificateBody; + QByteArray mSignature; +}; + +} // namespace governikus diff --git a/src/card/base/PersoSimWorkaround.h b/src/card/base/PersoSimWorkaround.h index b784935..ad626fc 100644 --- a/src/card/base/PersoSimWorkaround.h +++ b/src/card/base/PersoSimWorkaround.h @@ -7,7 +7,7 @@ #pragma once #include "CardConnectionWorker.h" -#include "Commands.h" +#include "SelectBuilder.h" #include diff --git a/src/card/base/PinModify.h b/src/card/base/PinModify.h index 51a17b3..3eb9e9a 100644 --- a/src/card/base/PinModify.h +++ b/src/card/base/PinModify.h @@ -26,8 +26,8 @@ class PinModify QByteArray createPinModificationDataStructure(ProtocolType pType) const; public: - PinModify(quint8 pTimeoutSeconds); - PinModify(const QByteArray& pRemoteInputData); + explicit PinModify(quint8 pTimeoutSeconds); + explicit PinModify(const QByteArray& pRemoteInputData); quint8 getTimeoutSeconds() const; diff --git a/src/card/base/PinModifyOutput.cpp b/src/card/base/PinModifyOutput.cpp index 28d8c3f..459076b 100644 --- a/src/card/base/PinModifyOutput.cpp +++ b/src/card/base/PinModifyOutput.cpp @@ -33,7 +33,7 @@ CardReturnCode PinModifyOutput::statusCodeToCardReturnCode(const QByteArray& pDa return CardReturnCode::UNKNOWN; } - const quint16 statusCode = qFromBigEndian(pData.data()); + const auto statusCode = qFromBigEndian(pData.data()); const StatusCode errorCode = Enum::isValue(statusCode) ? StatusCode(statusCode) : StatusCode::INVALID; switch (errorCode) { diff --git a/src/card/base/PinModifyOutput.h b/src/card/base/PinModifyOutput.h index bb2c5be..d83b104 100644 --- a/src/card/base/PinModifyOutput.h +++ b/src/card/base/PinModifyOutput.h @@ -22,7 +22,7 @@ class PinModifyOutput public: PinModifyOutput(); - PinModifyOutput(const ResponseApdu& pResponseApdu); + explicit PinModifyOutput(const ResponseApdu& pResponseApdu); CardReturnCode getReturnCode() const; const ResponseApdu& getResponseApdu() const; diff --git a/src/card/base/ReadBinaryBuilder.cpp b/src/card/base/ReadBinaryBuilder.cpp new file mode 100644 index 0000000..6065685 --- /dev/null +++ b/src/card/base/ReadBinaryBuilder.cpp @@ -0,0 +1,25 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "ReadBinaryBuilder.h" + +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + +ReadBinaryBuilder::ReadBinaryBuilder(uint pOffset, int pLe) + : CommandApduBuilder() + , mOffset(pOffset) + , mLe(pLe) +{ +} + + +CommandApdu ReadBinaryBuilder::build() +{ + static const char INS = char(0xB0); + return CommandApdu(CommandApdu::CLA, INS, static_cast((mOffset & 0xff00) >> 8), static_cast(mOffset & 0xff), QByteArray(), mLe); +} diff --git a/src/card/base/ReadBinaryBuilder.h b/src/card/base/ReadBinaryBuilder.h new file mode 100644 index 0000000..b3a4a1a --- /dev/null +++ b/src/card/base/ReadBinaryBuilder.h @@ -0,0 +1,26 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApduBuilder.h" + +class test_Commands; + +namespace governikus +{ + +class ReadBinaryBuilder + : public CommandApduBuilder +{ + private: + uint mOffset; + int mLe; + + public: + explicit ReadBinaryBuilder(uint pOffset, int pLe); + CommandApdu build() override; +}; + +} // namespace governikus diff --git a/src/card/base/Reader.cpp b/src/card/base/Reader.cpp index 16f09fd..aa87303 100644 --- a/src/card/base/Reader.cpp +++ b/src/card/base/Reader.cpp @@ -2,10 +2,11 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ +#include "Reader.h" #include "asn1/PaceInfo.h" #include "CardConnectionWorker.h" -#include "Reader.h" +#include "MSEBuilder.h" #include @@ -74,10 +75,7 @@ void Reader::update() CardReturnCode Reader::updateRetryCounter(QSharedPointer pCardConnectionWorker) { - int newRetryCounter = -1; - bool newPinDeactivated = false; - - CardReturnCode returnCode = getRetryCounter(pCardConnectionWorker, newRetryCounter, newPinDeactivated); + auto [returnCode, newRetryCounter, newPinDeactivated] = getRetryCounter(pCardConnectionWorker); if (returnCode == CardReturnCode::OK) { bool emitSignal = mReaderInfo.isRetryCounterDetermined() && ((newRetryCounter != mReaderInfo.getRetryCounter()) || (newPinDeactivated != mReaderInfo.isPinDeactivated())); @@ -96,12 +94,12 @@ CardReturnCode Reader::updateRetryCounter(QSharedPointer p } -CardReturnCode Reader::getRetryCounter(QSharedPointer pCardConnectionWorker, int& pRetryCounter, bool& pPinDeactivated) +Reader::RetryCounterResult Reader::getRetryCounter(QSharedPointer pCardConnectionWorker) { if (!mReaderInfo.getCardInfo().getEfCardAccess()) { qCCritical(card) << "Cannot get EF.CardAccess"; - return CardReturnCode::COMMAND_FAILED; + return {CardReturnCode::COMMAND_FAILED}; } // we don't need to establish PACE with this protocol (i.e. we don't need to support it), so we just take the fist one @@ -119,20 +117,19 @@ CardReturnCode Reader::getRetryCounter(QSharedPointer pCar CardReturnCode returnCode = pCardConnectionWorker->transmit(mseBuilder.build(), mseSetAtResponse); if (returnCode != CardReturnCode::OK) { - return returnCode; + return {returnCode}; } const StatusCode statusCode = mseSetAtResponse.getReturnCode(); qCDebug(card) << "StatusCode:" << statusCode; if (statusCode == StatusCode::INVALID) { - return CardReturnCode::COMMAND_FAILED; + return {CardReturnCode::COMMAND_FAILED}; } - pRetryCounter = mseSetAtResponse.getRetryCounter(); - pPinDeactivated = statusCode == StatusCode::PIN_DEACTIVATED; - - return CardReturnCode::OK; + const int retryCounter = mseSetAtResponse.getRetryCounter(); + const bool pinDeactivated = statusCode == StatusCode::PIN_DEACTIVATED; + return {CardReturnCode::OK, retryCounter, pinDeactivated}; } diff --git a/src/card/base/Reader.h b/src/card/base/Reader.h index 1352aa0..76fcbf4 100644 --- a/src/card/base/Reader.h +++ b/src/card/base/Reader.h @@ -11,8 +11,6 @@ #include #include -class test_Reader; - namespace governikus { @@ -21,22 +19,28 @@ class Reader { Q_OBJECT - protected: + public: enum class CardEvent { NONE, CARD_INSERTED, CARD_REMOVED, }; + protected: ReaderInfo mReaderInfo; int mTimerId; void timerEvent(QTimerEvent* pEvent) override; private: - friend class ::test_Reader; virtual CardEvent updateCard() = 0; - CardReturnCode getRetryCounter(QSharedPointer pCardConnectionWorker, int& pRetryCounter, bool& pPinDeactivated); + struct RetryCounterResult + { + CardReturnCode cardReturnCode = CardReturnCode::COMMAND_FAILED; + int retryCounter = -1; + bool pinDeactivated = false; + }; + RetryCounterResult getRetryCounter(QSharedPointer pCardConnectionWorker); void fireUpdateSignal(CardEvent pCardEvent); @@ -94,7 +98,10 @@ class ConnectableReader virtual ~ConnectableReader() override = default; virtual void connectReader() = 0; - virtual void disconnectReader() = 0; + virtual void disconnectReader(const QString& pError = QString()) = 0; + + Q_SIGNALS: + void fireReaderDisconnected(); }; } // namespace governikus diff --git a/src/card/base/ReaderFilter.h b/src/card/base/ReaderFilter.h index a6f344a..39d3bb4 100644 --- a/src/card/base/ReaderFilter.h +++ b/src/card/base/ReaderFilter.h @@ -29,13 +29,13 @@ class ReaderFilter Q_DECLARE_FLAGS(FilterTypes, FilterType) private: - ReaderFilter::FilterTypes mFilterType; + const ReaderFilter::FilterTypes mFilterType; const QVector mPluginTypes; public: ReaderFilter(); - ReaderFilter(const QVector& pPluginTypes); ReaderFilter(const ReaderFilter::FilterType pFilterType); + explicit ReaderFilter(const QVector& pPluginTypes); QVector apply(const QVector& pPluginType) const; QVector apply(const QVector& pInputList) const; diff --git a/src/card/base/ReaderInfo.h b/src/card/base/ReaderInfo.h index 82a7adf..756d012 100644 --- a/src/card/base/ReaderInfo.h +++ b/src/card/base/ReaderInfo.h @@ -62,6 +62,12 @@ class ReaderInfo } + bool hasPassport() const + { + return mCardInfo.isPassport(); + } + + int getRetryCounter() const { return mCardInfo.getRetryCounter(); diff --git a/src/card/base/ReaderManager.cpp b/src/card/base/ReaderManager.cpp index a803e8f..9f17320 100644 --- a/src/card/base/ReaderManager.cpp +++ b/src/card/base/ReaderManager.cpp @@ -119,14 +119,15 @@ void ReaderManager::startScan(ReaderManagerPlugInType pType, bool pAutoConnect) void ReaderManager::startScanAll(bool pAutoConnect) { - for (const auto& plugInType : Enum::getList()) + const auto list = Enum::getList(); + for (const auto& plugInType : list) { startScan(plugInType, pAutoConnect); } } -void ReaderManager::stopScan(ReaderManagerPlugInType pType) +void ReaderManager::stopScan(ReaderManagerPlugInType pType, const QString& pError) { const QMutexLocker mutexLocker(&mMutex); @@ -137,24 +138,35 @@ void ReaderManager::stopScan(ReaderManagerPlugInType pType) } QMetaObject::invokeMethod(mWorker.data(), [ = ] { - mWorker->stopScan(pType); + mWorker->stopScan(pType, pError); }, Qt::QueuedConnection); } void ReaderManager::stopScanAll() { - for (const auto& plugInType : Enum::getList()) + const auto list = Enum::getList(); + for (const auto& plugInType : list) { stopScan(plugInType); } } -bool ReaderManager::isScanRunning() +bool ReaderManager::isScanRunning() const { bool running = false; - QMetaObject::invokeMethod(mWorker.data(), &ReaderManagerWorker::isScanRunning, Qt::BlockingQueuedConnection, &running); + QMetaObject::invokeMethod(mWorker.data(), qOverload<>(&ReaderManagerWorker::isScanRunning), Qt::BlockingQueuedConnection, &running); + return running; +} + + +bool ReaderManager::isScanRunning(ReaderManagerPlugInType pType) const +{ + bool running = false; + QMetaObject::invokeMethod(mWorker.data(), [ = ] { + return mWorker->isScanRunning(pType); + }, Qt::BlockingQueuedConnection, &running); return running; } @@ -171,7 +183,7 @@ QVector ReaderManager::getPlugInInfos() const QVector ReaderManager::getReaderInfos(ReaderManagerPlugInType pType) const { - return getReaderInfos(QVector {pType}); + return getReaderInfos(ReaderFilter({pType})); } @@ -207,34 +219,6 @@ void ReaderManager::updateReaderInfo(const QString& pReaderName) } -void ReaderManager::connectReader(const QString& pReaderName) -{ - const QMutexLocker mutexLocker(&mMutex); - - QMetaObject::invokeMethod(mWorker.data(), [ = ] { - mWorker->connectReader(pReaderName); - }, Qt::QueuedConnection); -} - - -void ReaderManager::disconnectReader(const QString& pReaderName) -{ - const QMutexLocker mutexLocker(&mMutex); - - QMetaObject::invokeMethod(mWorker.data(), [ = ] { - mWorker->disconnectReader(pReaderName); - }, Qt::QueuedConnection); -} - - -void ReaderManager::disconnectAllReaders() -{ - const QMutexLocker mutexLocker(&mMutex); - - QMetaObject::invokeMethod(mWorker.data(), &ReaderManagerWorker::disconnectAllReaders, Qt::QueuedConnection); -} - - void ReaderManager::updateRetryCounters() { const QMutexLocker mutexLocker(&mMutex); diff --git a/src/card/base/ReaderManager.h b/src/card/base/ReaderManager.h index 314dc26..3a64e28 100644 --- a/src/card/base/ReaderManager.h +++ b/src/card/base/ReaderManager.h @@ -58,14 +58,19 @@ class ReaderManager /*! * Queries if any plugin is currently scanning */ - bool isScanRunning(); + bool isScanRunning() const; + + /*! + * Queries if a plugin with the requested type is currently scanning + */ + bool isScanRunning(ReaderManagerPlugInType pType) const; /*! * Stops started scan for devices. * Be aware that some plugins don't finish the whole scan if you * abort it with stopScan! */ - void stopScan(ReaderManagerPlugInType pType); + void stopScan(ReaderManagerPlugInType pType, const QString& pError = QString()); QVector getPlugInInfos() const; QVector getReaderInfos(ReaderManagerPlugInType pType) const; @@ -82,7 +87,7 @@ class ReaderManager template QMetaObject::Connection callCreateCardConnectionCommand(const QString& pReaderName, const typename QtPrivate::FunctionPointer::Object* pReceiver, T pSlot) { - CreateCardConnectionCommand* command = new CreateCardConnectionCommand(pReaderName, mWorker); + auto* command = new CreateCardConnectionCommand(pReaderName, mWorker); QMetaObject::Connection connection = connect(command, &CreateCardConnectionCommand::fireCommandDone, pReceiver, pSlot, Qt::UniqueConnection); if (connection) { @@ -90,7 +95,7 @@ class ReaderManager } else { - qCritical() << "Cannot invoke CreateCardConnectionCommand command"; + qCCritical(card) << "Cannot invoke CreateCardConnectionCommand command"; command->deleteLater(); } @@ -98,9 +103,6 @@ class ReaderManager } - void connectReader(const QString& pReaderName); - void disconnectReader(const QString& pReaderName); - void disconnectAllReaders(); void updateRetryCounters(); Q_SIGNALS: diff --git a/src/card/base/ReaderManagerPlugIn.cpp b/src/card/base/ReaderManagerPlugIn.cpp index 47508bd..c493e99 100644 --- a/src/card/base/ReaderManagerPlugIn.cpp +++ b/src/card/base/ReaderManagerPlugIn.cpp @@ -18,11 +18,21 @@ ReaderManagerPlugIn::ReaderManagerPlugIn(ReaderManagerPlugInType pPlugInType, void ReaderManagerPlugIn::startScan(bool /*pAutoConnect*/) { - mScanRunning = true; + if (!mScanRunning) + { + mScanRunning = true; + Q_EMIT fireStatusChanged(mInfo); + } } -void ReaderManagerPlugIn::stopScan() +void ReaderManagerPlugIn::stopScan(const QString& pError) { - mScanRunning = false; + Q_UNUSED(pError); + + if (mScanRunning) + { + mScanRunning = false; + Q_EMIT fireStatusChanged(mInfo); + } } diff --git a/src/card/base/ReaderManagerPlugIn.h b/src/card/base/ReaderManagerPlugIn.h index acc6d10..659d1f1 100644 --- a/src/card/base/ReaderManagerPlugIn.h +++ b/src/card/base/ReaderManagerPlugIn.h @@ -28,7 +28,7 @@ class ReaderManagerPlugIn bool mScanRunning; protected: - void setReaderInfoEnabled(bool pEnabled) + void setPlugInEnabled(bool pEnabled) { if (mInfo.isEnabled() != pEnabled) { @@ -38,13 +38,13 @@ class ReaderManagerPlugIn } - void setReaderInfoAvailable(bool pAvailable) + void setPlugInAvailable(bool pAvailable) { mInfo.setAvailable(pAvailable); } - void setReaderInfoResponding(bool pResponding) + void setPlugInResponding(bool pResponding) { if (mInfo.isResponding() != pResponding) { @@ -54,7 +54,7 @@ class ReaderManagerPlugIn } - void setReaderInfoValue(ReaderManagerPlugInInfo::Key pKey, const QVariant& pValue) + void setPlugInValue(ReaderManagerPlugInInfo::Key pKey, const QVariant& pValue) { mInfo.setValue(pKey, pValue); } @@ -93,7 +93,7 @@ class ReaderManagerPlugIn virtual void startScan(bool pAutoConnect); - virtual void stopScan(); + virtual void stopScan(const QString& pError = QString()); Q_SIGNALS: void fireStatusChanged(const ReaderManagerPlugInInfo& pInfo); diff --git a/src/card/base/ReaderManagerWorker.cpp b/src/card/base/ReaderManagerWorker.cpp index 1f06af5..37184f4 100644 --- a/src/card/base/ReaderManagerWorker.cpp +++ b/src/card/base/ReaderManagerWorker.cpp @@ -115,7 +115,7 @@ void ReaderManagerWorker::startScan(ReaderManagerPlugInType pType, bool pAutoCon } -void ReaderManagerWorker::stopScan(ReaderManagerPlugInType pType) +void ReaderManagerWorker::stopScan(ReaderManagerPlugInType pType, const QString& pError) { Q_ASSERT(QObject::thread() == QThread::currentThread()); @@ -124,7 +124,7 @@ void ReaderManagerWorker::stopScan(ReaderManagerPlugInType pType) if (plugin->getInfo().getPlugInType() == pType) { qCDebug(card) << "Stop scan on plugin:" << plugin->metaObject()->className(); - plugin->stopScan(); + plugin->stopScan(pError); } } } @@ -145,13 +145,28 @@ bool ReaderManagerWorker::isScanRunning() const } +bool ReaderManagerWorker::isScanRunning(ReaderManagerPlugInType pType) const +{ + Q_ASSERT(QObject::thread() == QThread::currentThread()); + + for (const auto& plugin : qAsConst(mPlugIns)) + { + if (plugin->getInfo().getPlugInType() == pType && plugin->isScanRunning()) + { + return true; + } + } + return false; +} + + QVector ReaderManagerWorker::getPlugInInfos() const { Q_ASSERT(QObject::thread() == QThread::currentThread()); QVector infos; infos.reserve(mPlugIns.size()); - for (const ReaderManagerPlugIn* plugIn : mPlugIns) + for (const ReaderManagerPlugIn* const plugIn : qAsConst(mPlugIns)) { infos += plugIn->getInfo(); } @@ -169,7 +184,7 @@ QVector ReaderManagerWorker::getReaderInfos(const ReaderFilter& pFil for (const auto& plugIn : plugIns) { const auto& readerList = plugIn->getReaders(); - for (const Reader* reader : readerList) + for (const Reader* const reader : readerList) { list += reader->getReaderInfo(); } @@ -182,7 +197,7 @@ ReaderInfo ReaderManagerWorker::getReaderInfo(const QString& pReaderName) const { Q_ASSERT(QObject::thread() == QThread::currentThread()); - const Reader* reader = getReader(pReaderName); + const Reader* const reader = getReader(pReaderName); return reader ? reader->getReaderInfo() : ReaderInfo(pReaderName); } @@ -192,7 +207,7 @@ void ReaderManagerWorker::updateReaderInfo(const QString& pReaderName) Q_ASSERT(QObject::thread() == QThread::currentThread()); Reader* reader = getReader(pReaderName); - if (!reader) + if (reader == nullptr) { qCWarning(card) << "Requested reader does not exist:" << pReaderName; return; @@ -235,39 +250,6 @@ void ReaderManagerWorker::createCardConnectionWorker(const QString& pReaderName) } -void ReaderManagerWorker::connectReader(const QString& pReaderName) -{ - Q_ASSERT(QObject::thread() == QThread::currentThread()); - - if (ConnectableReader* reader = qobject_cast(getReader(pReaderName))) - { - reader->connectReader(); - } -} - - -void ReaderManagerWorker::disconnectReader(const QString& pReaderName) -{ - Q_ASSERT(QObject::thread() == QThread::currentThread()); - - if (ConnectableReader* reader = qobject_cast(getReader(pReaderName))) - { - reader->disconnectReader(); - } -} - - -void ReaderManagerWorker::disconnectAllReaders() -{ - Q_ASSERT(QObject::thread() == QThread::currentThread()); - - for (auto& info : getReaderInfos()) - { - disconnectReader(info.getName()); - } -} - - void ReaderManagerWorker::updateRetryCounters() { Q_ASSERT(QObject::thread() == QThread::currentThread()); diff --git a/src/card/base/ReaderManagerWorker.h b/src/card/base/ReaderManagerWorker.h index e3fc007..458d4ac 100644 --- a/src/card/base/ReaderManagerWorker.h +++ b/src/card/base/ReaderManagerWorker.h @@ -34,18 +34,16 @@ class ReaderManagerWorker ~ReaderManagerWorker(); Q_INVOKABLE void startScan(ReaderManagerPlugInType pType, bool pAutoConnect); - Q_INVOKABLE void stopScan(ReaderManagerPlugInType pType); + Q_INVOKABLE void stopScan(ReaderManagerPlugInType pType, const QString& pError); Q_INVOKABLE bool isScanRunning() const; + Q_INVOKABLE bool isScanRunning(ReaderManagerPlugInType pType) const; Q_INVOKABLE QVector getPlugInInfos() const; Q_INVOKABLE QVector getReaderInfos(const ReaderFilter& pFilter = ReaderFilter()) const; Q_INVOKABLE ReaderInfo getReaderInfo(const QString& pReaderName) const; Q_INVOKABLE void updateReaderInfo(const QString& pReaderName); Q_INVOKABLE void createCardConnectionWorker(const QString& pReaderName); - Q_INVOKABLE void connectReader(const QString& pReaderName); - Q_INVOKABLE void disconnectReader(const QString& pReaderName); Q_INVOKABLE void updateRetryCounters(); - Q_INVOKABLE void disconnectAllReaders(); Q_SIGNALS: void firePluginAdded(const ReaderManagerPlugInInfo& pInfo); diff --git a/src/card/base/ResetRetryCounterBuilder.cpp b/src/card/base/ResetRetryCounterBuilder.cpp new file mode 100644 index 0000000..e477f4c --- /dev/null +++ b/src/card/base/ResetRetryCounterBuilder.cpp @@ -0,0 +1,29 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "ResetRetryCounterBuilder.h" + +#include + +using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + + +ResetRetryCounterBuilder::ResetRetryCounterBuilder(const QByteArray& pPin) + : CommandApduBuilder() + , mPin(pPin) +{ +} + + +CommandApdu ResetRetryCounterBuilder::build() +{ + static const char INS = 0x2c; + // P1: 2 (change), 3 (unblock) + const char p1 = mPin.isNull() ? char(3) : char(2); + // P2: 3 (PIN) (2 (CAN) -- not used) + // data: new PIN, when changing + return CommandApdu(CommandApdu::CLA, INS, p1, 3, mPin); +} diff --git a/src/card/base/ResetRetryCounterBuilder.h b/src/card/base/ResetRetryCounterBuilder.h new file mode 100644 index 0000000..a7e9059 --- /dev/null +++ b/src/card/base/ResetRetryCounterBuilder.h @@ -0,0 +1,25 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApduBuilder.h" + +class test_Commands; + +namespace governikus +{ + +class ResetRetryCounterBuilder + : public CommandApduBuilder +{ + public: + explicit ResetRetryCounterBuilder(const QByteArray& pPin = QByteArray()); + CommandApdu build() override; + + private: + QByteArray mPin; +}; + +} // namespace governikus diff --git a/src/card/base/ResponseApdu.cpp b/src/card/base/ResponseApdu.cpp index 465d9d7..d2f12fe 100644 --- a/src/card/base/ResponseApdu.cpp +++ b/src/card/base/ResponseApdu.cpp @@ -16,7 +16,7 @@ ResponseApdu::ResponseApdu(StatusCode pStatusCode) { char buffer[2]; qToBigEndian(Enum::getValue(pStatusCode), buffer); - setBuffer(QByteArray(buffer, 2)); + ResponseApdu::setBuffer(QByteArray(buffer, 2)); } @@ -26,11 +26,6 @@ ResponseApdu::ResponseApdu(const QByteArray& pBuffer) } -ResponseApdu::~ResponseApdu() -{ -} - - void ResponseApdu::setBuffer(const QByteArray& pBuffer) { mBuffer = pBuffer; @@ -151,6 +146,7 @@ CardReturnCode ResponseApdu::getCardReturnCode() const } Q_UNREACHABLE(); + return CardReturnCode::UNDEFINED; } diff --git a/src/card/base/ResponseApdu.h b/src/card/base/ResponseApdu.h index 1ce1613..d13f80e 100644 --- a/src/card/base/ResponseApdu.h +++ b/src/card/base/ResponseApdu.h @@ -86,9 +86,9 @@ class ResponseApdu static const int RETURN_CODE_LENGTH = 2; public: - ResponseApdu(StatusCode pStatusCode); - ResponseApdu(const QByteArray& pBuffer = QByteArray()); - virtual ~ResponseApdu(); + explicit ResponseApdu(StatusCode pStatusCode); + explicit ResponseApdu(const QByteArray& pBuffer = QByteArray()); + virtual ~ResponseApdu() = default; virtual void setBuffer(const QByteArray& pBuffer); QByteArray getData() const; diff --git a/src/card/base/SecureMessagingResponse.h b/src/card/base/SecureMessagingResponse.h index 8161609..667bc08 100644 --- a/src/card/base/SecureMessagingResponse.h +++ b/src/card/base/SecureMessagingResponse.h @@ -7,8 +7,7 @@ #pragma once #include "asn1/ASN1TemplateUtil.h" -#include "Commands.h" - +#include "ResponseApdu.h" namespace governikus { @@ -56,7 +55,7 @@ class SecureMessagingResponse Q_DISABLE_COPY(SecureMessagingResponse) public: - SecureMessagingResponse(const QByteArray& pBuffer); + explicit SecureMessagingResponse(const QByteArray& pBuffer); virtual ~SecureMessagingResponse(); /*! diff --git a/src/card/base/SelectBuilder.cpp b/src/card/base/SelectBuilder.cpp new file mode 100644 index 0000000..f268c69 --- /dev/null +++ b/src/card/base/SelectBuilder.cpp @@ -0,0 +1,27 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "SelectBuilder.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(card) + +using namespace governikus; + +/* + * SelectBuilder + */ +SelectBuilder::SelectBuilder(const FileRef& pFileRef) + : CommandApduBuilder() + , mFileRef(pFileRef) +{ +} + + +CommandApdu SelectBuilder::build() +{ + static const char INS = char(0xA4); + return CommandApdu(CommandApdu::CLA, INS, mFileRef.type, static_cast(P2::NONE), mFileRef.path); +} diff --git a/src/card/base/SelectBuilder.h b/src/card/base/SelectBuilder.h new file mode 100644 index 0000000..01a2e27 --- /dev/null +++ b/src/card/base/SelectBuilder.h @@ -0,0 +1,34 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "CommandApduBuilder.h" +#include "FileRef.h" + +namespace governikus +{ + +class SelectBuilder + : public CommandApduBuilder +{ + private: + const FileRef mFileRef; + + public: + enum class P1 : char + { + SELECT_MF = 0x00, CHILD_DF = 0x01, CHILD_EF = 0x02, PARENT_DF = 0x03, APPLICATION_ID = 0x04, ABS_PATH = 0x08, REL_PATH = 0x09, + }; + + enum class P2 : char + { + FCI = 0x00, FCP = 0x04, FMD = 0x08, NONE = 0x0c, + }; + + explicit SelectBuilder(const FileRef& pFileRef); + CommandApdu build() override; +}; + +} // namespace governikus diff --git a/src/card/base/SmartCardDefinitions.h b/src/card/base/SmartCardDefinitions.h index a80f5c2..98121d9 100644 --- a/src/card/base/SmartCardDefinitions.h +++ b/src/card/base/SmartCardDefinitions.h @@ -11,6 +11,7 @@ namespace governikus defineEnumType(CardType, NONE, UNKNOWN, + PASSPORT, EID_CARD) defineTypedEnumType(PacePasswordId, char, diff --git a/src/card/base/asn1/ASN1TemplateUtil.h b/src/card/base/asn1/ASN1TemplateUtil.h index 7dbf69f..4eee637 100644 --- a/src/card/base/asn1/ASN1TemplateUtil.h +++ b/src/card/base/asn1/ASN1TemplateUtil.h @@ -104,7 +104,7 @@ QSharedPointer decodeObject(const QByteArray& pData, bool pLogging = true) { ERR_clear_error(); const char* tmp = pData.constData(); - const unsigned char** dataPointer = reinterpret_cast(&tmp); + const auto** dataPointer = reinterpret_cast(&tmp); T* object = nullptr; if (!decodeAsn1Object(&object, dataPointer, pData.length()) && pLogging) diff --git a/src/card/base/asn1/ASN1Util.cpp b/src/card/base/asn1/ASN1Util.cpp index 0b14cd2..bac4c30 100644 --- a/src/card/base/asn1/ASN1Util.cpp +++ b/src/card/base/asn1/ASN1Util.cpp @@ -8,7 +8,9 @@ #include #include -#include +#include + +Q_DECLARE_LOGGING_CATEGORY(card) using namespace governikus; @@ -33,7 +35,7 @@ QByteArray Asn1ObjectUtil::convertTo(const ASN1_OBJECT* pAsn1Object) char buf[80] = {}; if (OBJ_obj2txt(buf, sizeof(buf), pAsn1Object, 1) == sizeof(buf)) { - qCritical() << "The OID may not fit into the given array, just return an empty string"; + qCCritical(card) << "The OID may not fit into the given array, just return an empty string"; return QByteArray(); } return QByteArray(buf); @@ -43,11 +45,11 @@ QByteArray Asn1ObjectUtil::convertTo(const ASN1_OBJECT* pAsn1Object) QByteArray Asn1ObjectUtil::getValue(const ASN1_OBJECT* pAsn1Object) { #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - return QByteArray(reinterpret_cast(pAsn1Object->data), pAsn1Object->length); + return QByteArray(reinterpret_cast(pAsn1Object->data), pAsn1Object->length); #else const size_t len = OBJ_length(pAsn1Object); - const unsigned char* data = OBJ_get0_data(pAsn1Object); + const unsigned char* const data = OBJ_get0_data(pAsn1Object); return QByteArray(reinterpret_cast(data), static_cast(len)); #endif @@ -136,7 +138,7 @@ QByteArray Asn1BCDDateUtil::convertFromQDateToUnpackedBCD(QDate pDate) if (aBCD.length() != 6) { - qCritical() << "Invalid date length."; + qCCritical(card) << "Invalid date length."; return QByteArray(); } @@ -154,13 +156,13 @@ QDate Asn1BCDDateUtil::convertFromUnpackedBCDToQDate(ASN1_OCTET_STRING* pDateBCD { if (pDateBCD == nullptr) { - qCritical() << "Date pointer null."; + qCCritical(card) << "Date pointer null."; return QDate(); } if (pDateBCD->length != 6) { - qCritical() << "Invalid date length."; + qCCritical(card) << "Invalid date length."; return QDate(); } diff --git a/src/card/base/asn1/AccessRoleAndRight.cpp b/src/card/base/asn1/AccessRoleAndRight.cpp index 933c207..4bd9787 100644 --- a/src/card/base/asn1/AccessRoleAndRight.cpp +++ b/src/card/base/asn1/AccessRoleAndRight.cpp @@ -4,7 +4,10 @@ #include "AccessRoleAndRight.h" -#include +#include + +Q_DECLARE_LOGGING_CATEGORY(card) + using namespace governikus; @@ -99,116 +102,152 @@ QString AccessRoleAndRightsUtil::toDisplayText(AccessRight pRight) switch (pRight) { case AccessRight::WRITE_DG17: + //: LABEL ALL_PLATFORMS return tr("WRITE_DG17"); case AccessRight::WRITE_DG18: + //: LABEL ALL_PLATFORMS return tr("WRITE_DG18"); case AccessRight::WRITE_DG19: + //: LABEL ALL_PLATFORMS return tr("WRITE_DG19"); case AccessRight::WRITE_DG20: + //: LABEL ALL_PLATFORMS return tr("WRITE_DG20"); case AccessRight::WRITE_DG21: + //: LABEL ALL_PLATFORMS return tr("WRITE_DG21"); /* 32-29: reserved for future use */ case AccessRight::READ_DG21: + //: LABEL ALL_PLATFORMS return tr("Optional data"); case AccessRight::READ_DG20: + //: LABEL ALL_PLATFORMS return tr("Residence permit II"); case AccessRight::READ_DG19: // "Auxiliary conditions" are replaced with "Residence permit I" in agreement with the BMI + //: LABEL ALL_PLATFORMS return tr("Residence permit I"); case AccessRight::READ_DG18: + //: LABEL ALL_PLATFORMS return tr("Community-ID"); case AccessRight::READ_DG17: + //: LABEL ALL_PLATFORMS return tr("Address"); case AccessRight::READ_DG16: + //: LABEL ALL_PLATFORMS return tr("RFU"); case AccessRight::READ_DG15: + //: LABEL ALL_PLATFORMS return tr("RFU"); case AccessRight::READ_DG14: + //: LABEL ALL_PLATFORMS return tr("RFU"); case AccessRight::READ_DG13: + //: LABEL ALL_PLATFORMS return tr("Birth name"); case AccessRight::READ_DG12: + //: LABEL ALL_PLATFORMS return tr("Optional data"); case AccessRight::READ_DG11: + //: LABEL ALL_PLATFORMS return tr("Gender"); case AccessRight::READ_DG10: + //: LABEL ALL_PLATFORMS return tr("Nationality"); case AccessRight::READ_DG09: + //: LABEL ALL_PLATFORMS return tr("Place of birth"); case AccessRight::READ_DG08: + //: LABEL ALL_PLATFORMS return tr("Date of birth"); case AccessRight::READ_DG07: + //: LABEL ALL_PLATFORMS return tr("Doctoral degree"); case AccessRight::READ_DG06: + //: LABEL ALL_PLATFORMS return tr("Religious / artistic name"); case AccessRight::READ_DG05: + //: LABEL ALL_PLATFORMS return tr("Family name"); case AccessRight::READ_DG04: + //: LABEL ALL_PLATFORMS return tr("Given name(s)"); case AccessRight::READ_DG03: + //: LABEL ALL_PLATFORMS return tr("Valid until"); case AccessRight::READ_DG02: + //: LABEL ALL_PLATFORMS return tr("Issuing country"); case AccessRight::READ_DG01: + //: LABEL ALL_PLATFORMS return tr("Document type"); case AccessRight::INSTALL_QUAL_CERT: + //: LABEL ALL_PLATFORMS return tr("Installation of qualified signature certificates"); case AccessRight::INSTALL_CERT: + //: LABEL ALL_PLATFORMS return tr("Installation of signature certificates"); case AccessRight::PIN_MANAGEMENT: + //: LABEL ALL_PLATFORMS return tr("PIN Management"); case AccessRight::CAN_ALLOWED: + //: LABEL ALL_PLATFORMS return tr("CAN allowed"); case AccessRight::PRIVILEGED_TERMINAL: + //: LABEL ALL_PLATFORMS return tr("Privileged terminal"); case AccessRight::RESTRICTED_IDENTIFICATION: + //: LABEL ALL_PLATFORMS return tr("Pseudonym"); case AccessRight::COMMUNITY_ID_VERIFICATION: + //: LABEL ALL_PLATFORMS return tr("Address verification"); case AccessRight::AGE_VERIFICATION: + //: LABEL ALL_PLATFORMS return tr("Age verification"); case AccessRight::RFU_29: case AccessRight::RFU_30: case AccessRight::RFU_31: case AccessRight::RFU_32: + //: LABEL ALL_PLATFORMS return tr("Unknown (reserved)"); } + //: LABEL ALL_PLATFORMS return tr("Unknown"); } @@ -218,7 +257,7 @@ QLatin1String AccessRoleAndRightsUtil::toTechnicalName(AccessRight pRight) const auto name = getEnumName(static_cast(pRight)); if (name.isEmpty()) { - qCritical() << "Requested AccessRight without mapping:" << pRight; + qCCritical(card) << "Requested AccessRight without mapping:" << pRight; } return name; } @@ -250,9 +289,9 @@ bool AccessRoleAndRightsUtil::fromTechnicalName(const QString& pStr, const std:: } -bool AccessRoleAndRightsUtil::fromTechnicalName(const char* pStr, const std::function& pFunc) +bool AccessRoleAndRightsUtil::fromTechnicalName(const char* const pStr, const std::function& pFunc) { - const AccessRightNames undefined = static_cast(UINT_MAX); + const auto undefined = static_cast(UINT_MAX); auto entry = Enum::fromString(pStr, undefined); if (entry != undefined) diff --git a/src/card/base/asn1/AccessRoleAndRight.h b/src/card/base/asn1/AccessRoleAndRight.h index 5373a54..dbf9ab1 100644 --- a/src/card/base/asn1/AccessRoleAndRight.h +++ b/src/card/base/asn1/AccessRoleAndRight.h @@ -107,7 +107,7 @@ class AccessRoleAndRightsUtil static QList mAllRights; static QList mAllDisplayedOrderedRights; AccessRoleAndRightsUtil() = delete; - static bool fromTechnicalName(const char* pStr, const std::function& pFunc); + static bool fromTechnicalName(const char* const pStr, const std::function& pFunc); static QStringList fromTechnicalName(const QStringList& pStr); public: diff --git a/src/card/base/asn1/AuthenticatedAuxiliaryData.h b/src/card/base/asn1/AuthenticatedAuxiliaryData.h index a4134fe..da0e528 100644 --- a/src/card/base/asn1/AuthenticatedAuxiliaryData.h +++ b/src/card/base/asn1/AuthenticatedAuxiliaryData.h @@ -42,12 +42,11 @@ namespace governikus */ -typedef struct auxdatatemplate_st +using AuxDataTemplate = struct auxdatatemplate_st { ASN1_OBJECT* mAuxId; ASN1_TYPE* mExtInfo; - -} AuxDataTemplate; +}; #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) DECLARE_STACK_OF(AuxDataTemplate) @@ -63,7 +62,7 @@ class AuthenticatedAuxiliaryData friend class QSharedPointer; QSharedPointer mData; - AuthenticatedAuxiliaryData(const QSharedPointer& pData); + explicit AuthenticatedAuxiliaryData(const QSharedPointer& pData); AuxDataTemplate* getAuxDataTemplateFor(KnownOIDs::AuxilaryData pData) const; QString getRequiredAge(const QDate& pEffectiveDate) const; diff --git a/src/card/base/asn1/CVCertificate.cpp b/src/card/base/asn1/CVCertificate.cpp index 735cb30..6ee6002 100644 --- a/src/card/base/asn1/CVCertificate.cpp +++ b/src/card/base/asn1/CVCertificate.cpp @@ -50,8 +50,8 @@ IMPLEMENT_ASN1_OBJECT(CVCertificate) int CVCertificate::decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ITEM* pIt, void* pExarg) { - Q_UNUSED(pIt); - Q_UNUSED(pExarg); + Q_UNUSED(pIt) + Q_UNUSED(pExarg) if (pOperation == ASN1_OP_D2I_POST) { if (auto cvc = reinterpret_cast(*pVal)) @@ -59,11 +59,11 @@ int CVCertificate::decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ cvc->mEcdsaSignature = EcUtil::create(ECDSA_SIG_new()); QByteArray sigValue = Asn1OctetStringUtil::getValue(cvc->mSignature); - const unsigned char* sig = reinterpret_cast(sigValue.data()); + const auto* const sig = reinterpret_cast(sigValue.data()); int siglen = sigValue.size(); - BIGNUM* r = BN_bin2bn(sig, siglen / 2, 0); - BIGNUM* s = BN_bin2bn(sig + (siglen / 2), siglen / 2, 0); + BIGNUM* r = BN_bin2bn(sig, siglen / 2, nullptr); + BIGNUM* s = BN_bin2bn(sig + (siglen / 2), siglen / 2, nullptr); #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) cvc->mEcdsaSignature.data()->r = r; diff --git a/src/card/base/asn1/CVCertificate.h b/src/card/base/asn1/CVCertificate.h index 9d6dbc0..049728d 100644 --- a/src/card/base/asn1/CVCertificate.h +++ b/src/card/base/asn1/CVCertificate.h @@ -43,7 +43,7 @@ struct SIGNATURE }; -typedef struct cvcertificate_st +using CVCertificate = struct cvcertificate_st { CVCertificateBody* mBody; SIGNATURE* mSignature; @@ -62,8 +62,7 @@ typedef struct cvcertificate_st bool isIssuedBy(const cvcertificate_st& pIssuer) const; static int decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ITEM* pIt, void* pExarg); - -} CVCertificate; +}; DECLARE_ASN1_FUNCTIONS(CVCertificate) diff --git a/src/card/base/asn1/CVCertificateBody.h b/src/card/base/asn1/CVCertificateBody.h index 7f747a8..87ca4cf 100644 --- a/src/card/base/asn1/CVCertificateBody.h +++ b/src/card/base/asn1/CVCertificateBody.h @@ -21,7 +21,7 @@ namespace governikus { -typedef struct CERTIFICATEEXTENSION_st +using CERTIFICATEEXTENSION = struct CERTIFICATEEXTENSION_st { ASN1_OBJECT* mOid; ASN1_OCTET_STRING* mObject1; @@ -32,7 +32,7 @@ typedef struct CERTIFICATEEXTENSION_st ASN1_OCTET_STRING* mObject6; ASN1_OCTET_STRING* mObject7; ASN1_OCTET_STRING* mObject8; -} CERTIFICATEEXTENSION; +}; DECLARE_ASN1_FUNCTIONS(CERTIFICATEEXTENSION) #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) @@ -41,7 +41,7 @@ DECLARE_STACK_OF(CERTIFICATEEXTENSION) DEFINE_STACK_OF(CERTIFICATEEXTENSION) #endif -typedef struct certificateprofilebody_st +using CVCertificateBody = struct certificateprofilebody_st { ASN1_OCTET_STRING* mCertificateProfileIdentifier; ASN1_STRING* mCertificationAuthorityReference; @@ -72,8 +72,7 @@ typedef struct certificateprofilebody_st QCryptographicHash::Algorithm getHashAlgorithm() const; QMap getExtensions() const; - -} CVCertificateBody; +}; DECLARE_ASN1_FUNCTIONS(CVCertificateBody) DECLARE_ASN1_OBJECT(CVCertificateBody) diff --git a/src/card/base/asn1/CVCertificateChain.h b/src/card/base/asn1/CVCertificateChain.h index 32a2668..444cd0e 100644 --- a/src/card/base/asn1/CVCertificateChain.h +++ b/src/card/base/asn1/CVCertificateChain.h @@ -10,7 +10,6 @@ #pragma once -#include "Commands.h" #include "CVCertificate.h" #include @@ -25,9 +24,8 @@ class CVCertificateChain bool mProductive; public: - CVCertificateChain(bool pProductive = true); - - CVCertificateChain(const QVector >& pCvcs, bool pProductive); + explicit CVCertificateChain(bool pProductive = true); + explicit CVCertificateChain(const QVector >& pCvcs, bool pProductive); /*! * Return the document verifier certificate. diff --git a/src/card/base/asn1/CVCertificateChainBuilder.h b/src/card/base/asn1/CVCertificateChainBuilder.h index e9ce8d5..3bb9546 100644 --- a/src/card/base/asn1/CVCertificateChainBuilder.h +++ b/src/card/base/asn1/CVCertificateChainBuilder.h @@ -28,12 +28,12 @@ class CVCertificateChainBuilder CVCertificateChain getChainForCertificationAuthority(const QByteArray& pCar) const; public: - CVCertificateChainBuilder(bool pProductive = true); + explicit CVCertificateChainBuilder(bool pProductive = true); /*! * Creates a new instance. All chains are build using the CVCs passed in as parameter. */ - CVCertificateChainBuilder(const QVector >& pCvcPool, bool pProductive); + explicit CVCertificateChainBuilder(const QVector >& pCvcPool, bool pProductive); /*! diff --git a/src/card/base/asn1/Chat.cpp b/src/card/base/asn1/Chat.cpp index f1c89f1..41e4a52 100644 --- a/src/card/base/asn1/Chat.cpp +++ b/src/card/base/asn1/Chat.cpp @@ -7,9 +7,9 @@ #include "Chat.h" #include "KnownOIDs.h" +#include -#include - +Q_DECLARE_LOGGING_CATEGORY(card) using namespace governikus; @@ -45,8 +45,8 @@ IMPLEMENT_ASN1_OBJECT(CHAT) int CHAT::decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ITEM* pIt, void* pExarg) { - Q_UNUSED(pIt); - Q_UNUSED(pExarg); + Q_UNUSED(pIt) + Q_UNUSED(pExarg) if (pOperation == ASN1_OP_D2I_POST) { if (auto chat = reinterpret_cast(*pVal)) @@ -54,7 +54,7 @@ int CHAT::decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ITEM* pIt if (chat->getTemplate().size() != 5) { // per definition it's an OCTET STRING of fixed SIZE(5) - qDebug() << "CHAT Template has wrong size" << chat->getTemplate().size(); + qCDebug(card) << "CHAT Template has wrong size" << chat->getTemplate().size(); CHAT_free(chat); *pVal = nullptr; return CB_ERROR; @@ -62,7 +62,7 @@ int CHAT::decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ITEM* pIt else if (chat->getType() != KnownOIDs::CHATType::ID_AT) { // currently we only support Authentication Terminals - qDebug() << "CHAT type is unsupported" << chat->getType(); + qCDebug(card) << "CHAT type is unsupported" << chat->getType(); CHAT_free(chat); *pVal = nullptr; return CB_ERROR; @@ -145,7 +145,7 @@ void CHAT::setAccessRole(AccessRole pRole) { if (pRole == AccessRole::UNKNOWN) { - qCritical() << "Cannot set" << pRole; + qCCritical(card) << "Cannot set" << pRole; return; } @@ -230,7 +230,7 @@ void chat_st::setTemplateBit(uint pBitIndex, bool pOn) { if (pBitIndex > 39) { - qCritical() << "Setting template bit > 39 not supported"; + qCCritical(card) << "Setting template bit > 39 not supported"; return; } if (mTemplate->length == 0) @@ -242,7 +242,7 @@ void chat_st::setTemplateBit(uint pBitIndex, bool pOn) } // because pBitIndex < 40, it follows that pBitIndex / 8 <= 4, so byteNumber has no underflow - quint8 byteNumber = static_cast(4 - (pBitIndex / 8)); + auto byteNumber = static_cast(4 - (pBitIndex / 8)); quint8 bitNumberInByte = pBitIndex % 8; if (pOn) { diff --git a/src/card/base/asn1/Chat.h b/src/card/base/asn1/Chat.h index 9a8adb1..70cab5a 100644 --- a/src/card/base/asn1/Chat.h +++ b/src/card/base/asn1/Chat.h @@ -62,7 +62,7 @@ namespace governikus */ -typedef struct chat_st +using CHAT = struct chat_st { ASN1_OBJECT* mType; ASN1_OCTET_STRING* mTemplate; @@ -96,9 +96,7 @@ typedef struct chat_st public: static int decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ITEM* pIt, void* pExarg); - - -} CHAT; +}; DECLARE_ASN1_FUNCTIONS(CHAT) DECLARE_ASN1_OBJECT(CHAT) diff --git a/src/card/base/asn1/EFCardSecurity.h b/src/card/base/asn1/EFCardSecurity.h index 18482a5..1be62ad 100644 --- a/src/card/base/asn1/EFCardSecurity.h +++ b/src/card/base/asn1/EFCardSecurity.h @@ -89,7 +89,7 @@ class EFCardSecurity friend class QSharedPointer; const QSharedPointer mSecurityInfos; - EFCardSecurity(const QSharedPointer& pSecurityInfos); + explicit EFCardSecurity(const QSharedPointer& pSecurityInfos); Q_DISABLE_COPY(EFCardSecurity) public: diff --git a/src/card/base/asn1/EcdsaPublicKey.cpp b/src/card/base/asn1/EcdsaPublicKey.cpp index 2f2caa0..b413002 100644 --- a/src/card/base/asn1/EcdsaPublicKey.cpp +++ b/src/card/base/asn1/EcdsaPublicKey.cpp @@ -22,14 +22,14 @@ namespace governikus int EcdsaPublicKey::decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ITEM* pIt, void* pExarg) { - Q_UNUSED(pIt); - Q_UNUSED(pExarg); + Q_UNUSED(pIt) + Q_UNUSED(pExarg) if (pOperation == ASN1_OP_D2I_POST) { if (auto ecdsaPublicKey = reinterpret_cast(*pVal)) { // According to TR-03110-3, chapter D.3.3: - // CONDITIONAL domain parameters MUST be either all present, except the cofactor, or all absent or all absent + // CONDITIONAL domain parameters MUST be either all present, except the cofactor, or all absent if ((ecdsaPublicKey->mPrimeModulus && ecdsaPublicKey->mFirstCoefficient && ecdsaPublicKey->mSecondCoefficient && ecdsaPublicKey->mBasePoint && ecdsaPublicKey->mOrderOfTheBasePoint) || (!ecdsaPublicKey->mPrimeModulus && !ecdsaPublicKey->mFirstCoefficient && !ecdsaPublicKey->mSecondCoefficient && !ecdsaPublicKey->mBasePoint && !ecdsaPublicKey->mOrderOfTheBasePoint)) { @@ -121,7 +121,7 @@ QSharedPointer EcdsaPublicKey::getEcKey() const void EcdsaPublicKey::initEcKey() { - if (!mPrimeModulus) + if (mPrimeModulus == nullptr) { return; } diff --git a/src/card/base/asn1/EcdsaPublicKey.h b/src/card/base/asn1/EcdsaPublicKey.h index db9c157..5c7b464 100644 --- a/src/card/base/asn1/EcdsaPublicKey.h +++ b/src/card/base/asn1/EcdsaPublicKey.h @@ -36,7 +36,7 @@ namespace governikus * UnsignedInteger -- see TR-03110 D.2.1.1 * */ -typedef struct ecdsapublickey_st +using EcdsaPublicKey = struct ecdsapublickey_st { ASN1_OBJECT* mObjectIdentifier; ASN1_OCTET_STRING* mPrimeModulus; @@ -66,8 +66,7 @@ typedef struct ecdsapublickey_st public: static int decodeCallback(int pOperation, ASN1_VALUE** pVal, const ASN1_ITEM* pIt, void* pExarg); - -} EcdsaPublicKey; +}; DECLARE_ASN1_FUNCTIONS(EcdsaPublicKey) diff --git a/src/card/base/asn1/KnownOIDs.cpp b/src/card/base/asn1/KnownOIDs.cpp index 2610819..efeaeba 100644 --- a/src/card/base/asn1/KnownOIDs.cpp +++ b/src/card/base/asn1/KnownOIDs.cpp @@ -25,6 +25,7 @@ QByteArray governikus::toByteArray(Base pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -40,6 +41,7 @@ QByteArray governikus::toByteArray(CertificateExtensions pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -58,6 +60,7 @@ QByteArray governikus::toByteArray(TermsOfUsageType pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -79,6 +82,7 @@ QByteArray governikus::toByteArray(CHATType pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -97,6 +101,7 @@ QByteArray governikus::toByteArray(AuxilaryData pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -118,6 +123,7 @@ QByteArray governikus::toByteArray(SecurityProtocol pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -159,6 +165,7 @@ QByteArray governikus::toByteArray(id_ca pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -183,6 +190,7 @@ QByteArray governikus::toByteArray(id_ta pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -198,6 +206,7 @@ QByteArray governikus::toByteArray(id_pk pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -239,6 +248,7 @@ QByteArray governikus::toByteArray(id_PACE::DH pValue) } Q_UNREACHABLE(); + return QByteArray(); } @@ -280,4 +290,5 @@ QByteArray governikus::toByteArray(id_PACE::ECDH pValue) } Q_UNREACHABLE(); + return QByteArray(); } diff --git a/src/card/base/asn1/PaceInfo.h b/src/card/base/asn1/PaceInfo.h index 5f7a570..496bfdf 100644 --- a/src/card/base/asn1/PaceInfo.h +++ b/src/card/base/asn1/PaceInfo.h @@ -63,7 +63,7 @@ class PaceInfo friend class QSharedPointer; const QSharedPointer mDelegate; - PaceInfo(const QSharedPointer& pDelegate); + explicit PaceInfo(const QSharedPointer& pDelegate); ASN1_OBJECT* getProtocolObjectIdentifier() const override; static bool acceptsProtocol(const ASN1_OBJECT* pObjectIdentifier); diff --git a/src/card/base/asn1/SecurityInfo.h b/src/card/base/asn1/SecurityInfo.h index 81e3129..2b5230a 100644 --- a/src/card/base/asn1/SecurityInfo.h +++ b/src/card/base/asn1/SecurityInfo.h @@ -48,7 +48,7 @@ class SecurityInfo friend class QSharedPointer; const QSharedPointer mDelegate; - SecurityInfo(const QSharedPointer& pDelegate); + explicit SecurityInfo(const QSharedPointer& pDelegate); /* * Sub classes must override this method to allow the base class to access diff --git a/src/card/base/asn1/SecurityInfos.h b/src/card/base/asn1/SecurityInfos.h index 55f8969..93531c0 100644 --- a/src/card/base/asn1/SecurityInfos.h +++ b/src/card/base/asn1/SecurityInfos.h @@ -17,7 +17,7 @@ namespace governikus { -typedef struct stack_st_securityinfo_st securityinfos_st; +using securityinfos_st = struct stack_st_securityinfo_st; DECLARE_ASN1_FUNCTIONS(securityinfos_st) DECLARE_ASN1_OBJECT(securityinfos_st) @@ -58,7 +58,7 @@ class SecurityInfos * * defined in TR 3110 Part 3 */ -typedef SecurityInfos EFCardAccess; +using EFCardAccess = SecurityInfos; } // namespace governikus diff --git a/src/card/base/asn1/SignatureChecker.cpp b/src/card/base/asn1/SignatureChecker.cpp index 89aead0..d70d399 100644 --- a/src/card/base/asn1/SignatureChecker.cpp +++ b/src/card/base/asn1/SignatureChecker.cpp @@ -66,7 +66,7 @@ bool SignatureChecker::checkSignature(const QSharedPointer& const QSharedPointer signingKey = EcUtil::create(EC_KEY_dup(pKey)); const QByteArray uncompPublicPoint = pSigningCert->getBody().getPublicKey().getUncompressedPublicPoint(); - const unsigned char* uncompPublicPointData = reinterpret_cast(uncompPublicPoint.constData()); + const auto* const uncompPublicPointData = reinterpret_cast(uncompPublicPoint.constData()); const auto uncompPublicPointLen = static_cast(uncompPublicPoint.size()); EC_POINT* publicPoint = EC_POINT_new(EC_KEY_get0_group(signingKey.data())); @@ -79,7 +79,7 @@ bool SignatureChecker::checkSignature(const QSharedPointer& EC_KEY_set_public_key(signingKey.data(), publicPoint); const QByteArray bodyHash = QCryptographicHash::hash(pCert->getRawBody(), pSigningCert->getBody().getHashAlgorithm()); - const unsigned char* dgst = reinterpret_cast(bodyHash.constData()); + const auto* const dgst = reinterpret_cast(bodyHash.constData()); const int dgstlen = bodyHash.size(); const int result = ECDSA_do_verify(dgst, dgstlen, pCert->getEcdsaSignature().data(), signingKey.data()); diff --git a/src/card/base/asn1/SignatureChecker.h b/src/card/base/asn1/SignatureChecker.h index 098310e..3e6b35b 100644 --- a/src/card/base/asn1/SignatureChecker.h +++ b/src/card/base/asn1/SignatureChecker.h @@ -20,7 +20,7 @@ class SignatureChecker bool checkSignature(const QSharedPointer& pCert, const QSharedPointer& pSigningCert, const EC_KEY* pKey); public: - SignatureChecker(const QVector >& pCertificateChain); + explicit SignatureChecker(const QVector >& pCertificateChain); ~SignatureChecker() = default; bool check(); diff --git a/src/card/base/command/BaseCardCommand.h b/src/card/base/command/BaseCardCommand.h index 101049a..c76ec14 100644 --- a/src/card/base/command/BaseCardCommand.h +++ b/src/card/base/command/BaseCardCommand.h @@ -11,7 +11,6 @@ #include -class test_BaseCardCommand; class test_CardConnection; namespace governikus @@ -23,7 +22,6 @@ class BaseCardCommand Q_OBJECT private: - friend class ::test_BaseCardCommand; friend class ::test_CardConnection; Q_INVOKABLE void execute(); @@ -31,7 +29,7 @@ class BaseCardCommand QSharedPointer mCardConnectionWorker; CardReturnCode mReturnCode; - BaseCardCommand(QSharedPointer pCardConnectionWorker); + explicit BaseCardCommand(QSharedPointer pCardConnectionWorker); virtual void internalExecute() = 0; virtual ~BaseCardCommand(); diff --git a/src/card/base/command/CreateCardConnectionCommand.cpp b/src/card/base/command/CreateCardConnectionCommand.cpp index 232f3b3..0de85b1 100644 --- a/src/card/base/command/CreateCardConnectionCommand.cpp +++ b/src/card/base/command/CreateCardConnectionCommand.cpp @@ -7,10 +7,14 @@ #include "Initializer.h" #include "ReaderManagerWorker.h" +#include #include +Q_DECLARE_LOGGING_CATEGORY(card) + using namespace governikus; + static Initializer::Entry X([] { qRegisterMetaType >("QSharedPointer"); }); @@ -45,7 +49,7 @@ void CreateCardConnectionCommand::execute() } else { - qCritical() << "Cannot invoke ReaderManagerWorker to create CardConnectionWorker"; + qCCritical(card) << "Cannot invoke ReaderManagerWorker to create CardConnectionWorker"; QSharedPointer command(this, &QObject::deleteLater); Q_EMIT fireCommandDone(command); } diff --git a/src/card/base/command/CreateCardConnectionCommand.h b/src/card/base/command/CreateCardConnectionCommand.h index 9940089..6f6bca0 100644 --- a/src/card/base/command/CreateCardConnectionCommand.h +++ b/src/card/base/command/CreateCardConnectionCommand.h @@ -33,7 +33,7 @@ class CreateCardConnectionCommand Q_INVOKABLE void execute(); public: - CreateCardConnectionCommand(const QString& pReaderName, const QPointer& pReaderManagerWorker); + explicit CreateCardConnectionCommand(const QString& pReaderName, const QPointer& pReaderManagerWorker); void run(); QSharedPointer getCardConnection() const; diff --git a/src/card/base/command/DestroyPaceChannelCommand.h b/src/card/base/command/DestroyPaceChannelCommand.h index a4cbb5e..b3610c8 100644 --- a/src/card/base/command/DestroyPaceChannelCommand.h +++ b/src/card/base/command/DestroyPaceChannelCommand.h @@ -26,7 +26,7 @@ class DestroyPaceChannelCommand virtual ~DestroyPaceChannelCommand() override = default; public: - DestroyPaceChannelCommand(QSharedPointer pCardConnectionWorker); + explicit DestroyPaceChannelCommand(QSharedPointer pCardConnectionWorker); }; } // namespace governikus diff --git a/src/card/base/command/DidAuthenticateEAC1Command.cpp b/src/card/base/command/DidAuthenticateEAC1Command.cpp index 8c4be37..d8c6879 100644 --- a/src/card/base/command/DidAuthenticateEAC1Command.cpp +++ b/src/card/base/command/DidAuthenticateEAC1Command.cpp @@ -2,9 +2,11 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ +#include "DidAuthenticateEAC1Command.h" + #include "BaseCardCommand.h" #include "CardConnection.h" -#include "DidAuthenticateEAC1Command.h" +#include "GetChallengeBuilder.h" #include @@ -24,12 +26,18 @@ DidAuthenticateEAC1Command::DidAuthenticateEAC1Command(QSharedPointertransmit(GetChallengeBuilder().build(), response); if (mReturnCode != CardReturnCode::OK || response.getReturnCode() != StatusCode::SUCCESS) { qCWarning(card) << "GetChallenge failed"; return; } - mChallenge = response.getChallenge(); + + if (response.getDataLength() != 8) + { + qCCritical(card) << "Challenge has wrong size. Expect 8 bytes, got" << response.getDataLength(); + } + + mChallenge = response.getData(); } diff --git a/src/card/base/command/DidAuthenticateEAC1Command.h b/src/card/base/command/DidAuthenticateEAC1Command.h index 934273e..1062a49 100644 --- a/src/card/base/command/DidAuthenticateEAC1Command.h +++ b/src/card/base/command/DidAuthenticateEAC1Command.h @@ -8,7 +8,6 @@ #include "asn1/Chat.h" #include "BaseCardCommand.h" -#include "Commands.h" class test_DidAuthenticateEAC1Command; class test_StateDidAuthenticateEac1; @@ -31,7 +30,7 @@ class DidAuthenticateEAC1Command virtual ~DidAuthenticateEAC1Command() override = default; public: - DidAuthenticateEAC1Command(QSharedPointer pCardConnectionWorker); + explicit DidAuthenticateEAC1Command(QSharedPointer pCardConnectionWorker); const QByteArray& getChallenge() const { diff --git a/src/card/base/command/DidAuthenticateEAC2Command.cpp b/src/card/base/command/DidAuthenticateEAC2Command.cpp index f53f578..ab30c38 100644 --- a/src/card/base/command/DidAuthenticateEAC2Command.cpp +++ b/src/card/base/command/DidAuthenticateEAC2Command.cpp @@ -7,8 +7,12 @@ #include "asn1/ChipAuthenticationInfo.h" #include "asn1/EFCardSecurity.h" #include "CardConnection.h" +#include "EABuilder.h" +#include "GABuilder.h" #include "GeneralAuthenticateResponse.h" #include "GlobalStatus.h" +#include "MSEBuilder.h" +#include "PSOBuilder.h" #include @@ -223,21 +227,24 @@ CardReturnCode DidAuthenticateEAC2Command::performChipAuthentication(QSharedPoin return CardReturnCode::PROTOCOL_ERROR; } - GAChipAuthenticationResponse gaResponse; GABuilder gaBuilder; gaBuilder.setCaEphemeralPublicKey(ephemeralPublicKey); qCDebug(card) << "Performing CA General Authenticate"; - returnCode = mCardConnectionWorker->transmit(gaBuilder.build(), gaResponse); + ResponseApdu responseApdu; + returnCode = mCardConnectionWorker->transmit(gaBuilder.build(), responseApdu); if (returnCode != CardReturnCode::OK) { return returnCode; } - if (gaResponse.getReturnCode() != StatusCode::SUCCESS) + + if (responseApdu.getReturnCode() != StatusCode::SUCCESS) { - qCWarning(card) << "CA General Authenticate failed:" << gaResponse.getReturnCode(); + qCWarning(card) << "CA General Authenticate failed:" << responseApdu.getReturnCode(); return CardReturnCode::PROTOCOL_ERROR; } + + GAChipAuthenticationResponse gaResponse(responseApdu); pNonceAsHex += gaResponse.getNonce().toHex(); pAuthTokenAsHex += gaResponse.getAuthenticationToken().toHex(); diff --git a/src/card/base/command/DidAuthenticateEAC2Command.h b/src/card/base/command/DidAuthenticateEAC2Command.h index ff36d33..1a81ca2 100644 --- a/src/card/base/command/DidAuthenticateEAC2Command.h +++ b/src/card/base/command/DidAuthenticateEAC2Command.h @@ -10,6 +10,7 @@ #include "BaseCardCommand.h" class test_CardConnection; +class test_DidAuthenticateEAC2Command; namespace governikus { @@ -21,6 +22,7 @@ class DidAuthenticateEAC2Command private: friend class ::test_CardConnection; + friend class ::test_DidAuthenticateEAC2Command; CVCertificateChain mCvcChain; QString mEphemeralPublicKeyAsHex; QString mSignatureAsHex; @@ -45,7 +47,7 @@ class DidAuthenticateEAC2Command virtual ~DidAuthenticateEAC2Command() override = default; public: - DidAuthenticateEAC2Command(QSharedPointer pCardConnectionWorker, + explicit DidAuthenticateEAC2Command(QSharedPointer pCardConnectionWorker, const CVCertificateChain& pCvcChain, const QString& pEphemeralPublicKeyAsHex, const QString& pSignatureAsHex, const QByteArray& pAuthenticatedAuxiliaryDataAsBinary); diff --git a/src/card/base/command/EstablishPaceChannelCommand.cpp b/src/card/base/command/EstablishPaceChannelCommand.cpp index 67eca9e..2cbe975 100644 --- a/src/card/base/command/EstablishPaceChannelCommand.cpp +++ b/src/card/base/command/EstablishPaceChannelCommand.cpp @@ -30,5 +30,6 @@ const EstablishPaceChannelOutput& EstablishPaceChannelCommand::getPaceOutput() c void EstablishPaceChannelCommand::internalExecute() { - mReturnCode = mCardConnectionWorker->establishPaceChannel(mPacePasswordId, mPacePassword, mEffectiveChat, mCertificateDescription, mPaceOutput); + mPaceOutput = mCardConnectionWorker->establishPaceChannel(mPacePasswordId, mPacePassword, mEffectiveChat, mCertificateDescription); + mReturnCode = mPaceOutput.getPaceReturnCode(); } diff --git a/src/card/base/command/EstablishPaceChannelCommand.h b/src/card/base/command/EstablishPaceChannelCommand.h index ad86568..cfccb58 100644 --- a/src/card/base/command/EstablishPaceChannelCommand.h +++ b/src/card/base/command/EstablishPaceChannelCommand.h @@ -9,6 +9,7 @@ #include "BaseCardCommand.h" class test_EstablishPaceChannelCommand; +class MockEstablishPaceChannelCommand; namespace governikus { @@ -20,6 +21,7 @@ class EstablishPaceChannelCommand private: friend class ::test_EstablishPaceChannelCommand; + friend class ::MockEstablishPaceChannelCommand; const PacePasswordId mPacePasswordId; const QString mPacePassword; @@ -32,7 +34,7 @@ class EstablishPaceChannelCommand virtual ~EstablishPaceChannelCommand() override = default; public: - EstablishPaceChannelCommand(QSharedPointer pCardConnectionWorker, + explicit EstablishPaceChannelCommand(QSharedPointer pCardConnectionWorker, PacePasswordId pPacePasswordId, const QString& pPacePassword, const QByteArray& pEffectiveChat, const QByteArray& pCertificateDescription); const EstablishPaceChannelOutput& getPaceOutput() const; diff --git a/src/card/base/command/SetEidPinCommand.h b/src/card/base/command/SetEidPinCommand.h index 7ab7cd8..85745a7 100644 --- a/src/card/base/command/SetEidPinCommand.h +++ b/src/card/base/command/SetEidPinCommand.h @@ -9,6 +9,7 @@ #include "BaseCardCommand.h" class test_SetEidPinCommand; +class MockSetEidPinCommand; namespace governikus { @@ -20,6 +21,7 @@ class SetEidPinCommand private: friend class ::test_SetEidPinCommand; + friend class ::MockSetEidPinCommand; QString mNewPin; quint8 mTimeoutSeconds; @@ -30,7 +32,7 @@ class SetEidPinCommand virtual ~SetEidPinCommand() override = default; public: - SetEidPinCommand(QSharedPointer pCardConnectionWorker, + explicit SetEidPinCommand(QSharedPointer pCardConnectionWorker, const QString& pNewPin, quint8 pTimeoutSeconds); const ResponseApdu& getResponseApdu() const; diff --git a/src/card/base/command/TransmitCommand.cpp b/src/card/base/command/TransmitCommand.cpp index 459a10c..e804767 100644 --- a/src/card/base/command/TransmitCommand.cpp +++ b/src/card/base/command/TransmitCommand.cpp @@ -18,7 +18,7 @@ using namespace governikus; TransmitCommand::TransmitCommand(QSharedPointer pCardConnectionWorker, const QVector& pInputApduInfos, - const QString pSlotHandle) + const QString& pSlotHandle) : BaseCardCommand(pCardConnectionWorker) , mInputApduInfos(pInputApduInfos) , mSlotHandle(pSlotHandle) diff --git a/src/card/base/command/TransmitCommand.h b/src/card/base/command/TransmitCommand.h index bd36101..9b572de 100644 --- a/src/card/base/command/TransmitCommand.h +++ b/src/card/base/command/TransmitCommand.h @@ -35,9 +35,9 @@ class TransmitCommand virtual ~TransmitCommand() override = default; public: - TransmitCommand(QSharedPointer pCardConnectionWorker, + explicit TransmitCommand(QSharedPointer pCardConnectionWorker, const QVector& pInputApduInfos, - const QString pSlotHandle); + const QString& pSlotHandle); const QByteArrayList& getOutputApduAsHex() const { diff --git a/src/card/base/command/UnblockPinCommand.cpp b/src/card/base/command/UnblockPinCommand.cpp index e3962e0..4f60bdd 100644 --- a/src/card/base/command/UnblockPinCommand.cpp +++ b/src/card/base/command/UnblockPinCommand.cpp @@ -4,6 +4,8 @@ #include "UnblockPinCommand.h" +#include "ResetRetryCounterBuilder.h" + using namespace governikus; @@ -29,8 +31,7 @@ void UnblockPinCommand::internalExecute() return; } - EstablishPaceChannelOutput output; - mReturnCode = mCardConnectionWorker->establishPaceChannel(PacePasswordId::PACE_PUK, mPuk, output); + mReturnCode = mCardConnectionWorker->establishPaceChannel(PacePasswordId::PACE_PUK, mPuk).getPaceReturnCode(); if (mReturnCode != CardReturnCode::OK) { return; diff --git a/src/card/base/command/UnblockPinCommand.h b/src/card/base/command/UnblockPinCommand.h index 318da12..264f565 100644 --- a/src/card/base/command/UnblockPinCommand.h +++ b/src/card/base/command/UnblockPinCommand.h @@ -27,7 +27,7 @@ class UnblockPinCommand virtual ~UnblockPinCommand() override = default; public: - UnblockPinCommand(QSharedPointer pCardConnectionWorker, const QString& pPuk); + explicit UnblockPinCommand(QSharedPointer pCardConnectionWorker, const QString& pPuk); }; diff --git a/src/card/base/command/UpdateRetryCounterCommand.h b/src/card/base/command/UpdateRetryCounterCommand.h index ae25ecb..b331309 100644 --- a/src/card/base/command/UpdateRetryCounterCommand.h +++ b/src/card/base/command/UpdateRetryCounterCommand.h @@ -26,7 +26,7 @@ class UpdateRetryCounterCommand virtual ~UpdateRetryCounterCommand() override = default; public: - UpdateRetryCounterCommand(QSharedPointer pCardConnectionWorker); + explicit UpdateRetryCounterCommand(QSharedPointer pCardConnectionWorker); }; diff --git a/src/card/base/pace/KeyAgreement.cpp b/src/card/base/pace/KeyAgreement.cpp index 8518faf..55d0c4b 100644 --- a/src/card/base/pace/KeyAgreement.cpp +++ b/src/card/base/pace/KeyAgreement.cpp @@ -6,7 +6,7 @@ #include "KeyAgreement.h" #include "asn1/PaceInfo.h" -#include "Commands.h" +#include "GABuilder.h" #include "GlobalStatus.h" #include "pace/CipherMac.h" #include "pace/ec/EcdhKeyAgreement.h" @@ -28,14 +28,14 @@ static QString getResponseErrorString(CardReturnCode pReturnCode, StatusCode pRe } -static CardOperationResult makeTransmitResult(CardReturnCode pReturnCode, +KeyAgreement::CardResult KeyAgreement::createTransmitResult(CardReturnCode pReturnCode, StatusCode pResponseReturnCode, const QByteArray& pResultData, - const QString& pLogMessage) + const char* pLogMessage) { if (pReturnCode == CardReturnCode::OK && pResponseReturnCode == StatusCode::SUCCESS) { - return CardOperationResult(pReturnCode, pResultData); + return {pReturnCode, pResultData}; } CardReturnCode newReturnCode = CardReturnCode::COMMAND_FAILED; @@ -49,7 +49,7 @@ static CardOperationResult makeTransmitResult(CardReturnCode pReturn } qCCritical(card).noquote() << pLogMessage << getResponseErrorString(newReturnCode, pResponseReturnCode); - return CardOperationResult(newReturnCode, QByteArray()); + return {newReturnCode}; } @@ -87,8 +87,8 @@ KeyAgreement::~KeyAgreement() KeyAgreementStatus KeyAgreement::perform(const QString& pPin) { - CardOperationResult nonceResult = determineNonce(pPin); - switch (nonceResult.getReturnCode()) + auto [returnCode, nonce] = determineNonce(pPin); + switch (returnCode) { case CardReturnCode::PROTOCOL_ERROR: return KeyAgreementStatus::PROTOCOL_ERROR; @@ -103,9 +103,8 @@ KeyAgreementStatus KeyAgreement::perform(const QString& pPin) {} } - QByteArray nonce = nonceResult.getPayload(); - CardOperationResult sharedSecretResult = determineSharedSecret(nonce); - switch (sharedSecretResult.getReturnCode()) + auto [sharedSecretReturnCode, sharedSecret] = determineSharedSecret(nonce); + switch (sharedSecretReturnCode) { case CardReturnCode::COMMAND_FAILED: return KeyAgreementStatus::COMMUNICATION_ERROR; @@ -120,7 +119,6 @@ KeyAgreementStatus KeyAgreement::perform(const QString& pPin) {} } - QByteArray sharedSecret = sharedSecretResult.getPayload(); mEncryptionKey = mKeyDerivationFunction.enc(sharedSecret); mMacKey = mKeyDerivationFunction.mac(sharedSecret); @@ -128,19 +126,18 @@ KeyAgreementStatus KeyAgreement::perform(const QString& pPin) } -CardOperationResult KeyAgreement::determineNonce(const QString& pPin) +KeyAgreement::CardResult KeyAgreement::determineNonce(const QString& pPin) { - CardOperationResult result = transmitGAEncryptedNonce(); - if (result.getReturnCode() != CardReturnCode::OK) + const auto result = transmitGAEncryptedNonce(); + if (result.mReturnCode != CardReturnCode::OK) { return result; } - QByteArray encryptedNonce = result.getPayload(); QByteArray symmetricKey = mKeyDerivationFunction.pi(pPin); SymmetricCipher nonceDecrypter(mPaceInfo->getProtocol(), symmetricKey); - return CardOperationResult(CardReturnCode::OK, nonceDecrypter.decrypt(encryptedNonce)); + return {CardReturnCode::OK, nonceDecrypter.decrypt(result.mData)}; } @@ -151,19 +148,21 @@ KeyAgreementStatus KeyAgreement::performMutualAuthenticate() QByteArray uncompressedCardPublicKey = getUncompressedCardPublicKey(); QByteArray mutualAuthenticationCardData = cmac.generate(uncompressedCardPublicKey); - QSharedPointer response = transmitGAMutualAuthentication(mutualAuthenticationCardData); - if (response->getReturnCode() == StatusCode::EMPTY) + const GAMutualAuthenticationResponse response = transmitGAMutualAuthentication(mutualAuthenticationCardData); + if (response.getReturnCode() == StatusCode::EMPTY) { return KeyAgreementStatus::RETRY_ALLOWED; } - else if (response->getReturnCode() == StatusCode::VERIFICATION_FAILED || - response->getReturnCode() == StatusCode::PIN_BLOCKED || - response->getReturnCode() == StatusCode::PIN_SUSPENDED || - response->getReturnCode() == StatusCode::PIN_RETRY_COUNT_2) + + if (response.getReturnCode() == StatusCode::VERIFICATION_FAILED || + response.getReturnCode() == StatusCode::PIN_BLOCKED || + response.getReturnCode() == StatusCode::PIN_SUSPENDED || + response.getReturnCode() == StatusCode::PIN_RETRY_COUNT_2) { return KeyAgreementStatus::FAILED; } - else if (response->getReturnCode() != StatusCode::SUCCESS) + + if (response.getReturnCode() != StatusCode::SUCCESS) { return KeyAgreementStatus::PROTOCOL_ERROR; } @@ -171,64 +170,67 @@ KeyAgreementStatus KeyAgreement::performMutualAuthenticate() QByteArray uncompressedTerminalPublicKey = getUncompressedTerminalPublicKey(); QByteArray mutualAuthenticationTerminalData = cmac.generate(uncompressedTerminalPublicKey); - if (mutualAuthenticationTerminalData != response->getAuthenticationToken()) + if (mutualAuthenticationTerminalData != response.getAuthenticationToken()) { qCCritical(card) << "Error on mutual authentication"; return KeyAgreementStatus::PROTOCOL_ERROR; } - mCarCurr = response->getCarCurr(); - mCarPrev = response->getCarPrev(); + mCarCurr = response.getCarCurr(); + mCarPrev = response.getCarPrev(); qCDebug(card) << "Successfully authenticated"; return KeyAgreementStatus::SUCCESS; } -CardOperationResult KeyAgreement::transmitGAEncryptedNonce() +KeyAgreement::CardResult KeyAgreement::transmitGAEncryptedNonce() { GABuilder builder(CommandApdu::CLA_COMMAND_CHAINING); - GAEncryptedNonceResponse response; + ResponseApdu responseApdu; - const CardReturnCode returnCode = mCardConnectionWorker->transmit(builder.build(), response); - return makeTransmitResult(returnCode, response.getReturnCode(), response.getEncryptedNonce(), QStringLiteral("Error on GA (Encrypted Nonce):")); + const CardReturnCode returnCode = mCardConnectionWorker->transmit(builder.build(), responseApdu); + GAEncryptedNonceResponse response(responseApdu); + return createTransmitResult(returnCode, response.getReturnCode(), response.getEncryptedNonce(), "Error on GA (Encrypted Nonce):"); } -CardOperationResult KeyAgreement::transmitGAEphemeralPublicKey(const QByteArray& pEphemeralPublicKey) +KeyAgreement::CardResult KeyAgreement::transmitGAEphemeralPublicKey(const QByteArray& pEphemeralPublicKey) { GABuilder commandBuilder(CommandApdu::CLA_COMMAND_CHAINING); commandBuilder.setPaceEphemeralPublicKey(pEphemeralPublicKey); - GAPerformKeyAgreementResponse response; + ResponseApdu responseApdu; - const CardReturnCode returnCode = mCardConnectionWorker->transmit(commandBuilder.build(), response); - return makeTransmitResult(returnCode, response.getReturnCode(), response.getEphemeralPublicKey(), QStringLiteral("Error on GA(Perform Key Agreement):")); + const CardReturnCode returnCode = mCardConnectionWorker->transmit(commandBuilder.build(), responseApdu); + const GAPerformKeyAgreementResponse response(responseApdu); + return createTransmitResult(returnCode, response.getReturnCode(), response.getEphemeralPublicKey(), "Error on GA(Perform Key Agreement):"); } -CardOperationResult KeyAgreement::transmitGAMappingData(const QByteArray& pMappingData) +KeyAgreement::CardResult KeyAgreement::transmitGAMappingData(const QByteArray& pMappingData) { // sende den PublicKey (D.3.4.) GABuilder commandBuilder(CommandApdu::CLA_COMMAND_CHAINING); commandBuilder.setPaceMappingData(pMappingData); - GAMapNonceResponse response; + ResponseApdu responseApdu; - const CardReturnCode returnCode = mCardConnectionWorker->transmit(commandBuilder.build(), response); - return makeTransmitResult(returnCode, response.getReturnCode(), response.getMappingData(), QStringLiteral("Error on GA(Mapping Data):")); + const CardReturnCode returnCode = mCardConnectionWorker->transmit(commandBuilder.build(), responseApdu); + const GAMapNonceResponse response(responseApdu); + return createTransmitResult(returnCode, response.getReturnCode(), response.getMappingData(), "Error on GA(Mapping Data):"); } -QSharedPointer KeyAgreement::transmitGAMutualAuthentication(const QByteArray& pMutualAuthenticationData) +GAMutualAuthenticationResponse KeyAgreement::transmitGAMutualAuthentication(const QByteArray& pMutualAuthenticationData) { GABuilder commandBuilder(CommandApdu::CLA); commandBuilder.setPaceAuthenticationToken(pMutualAuthenticationData); - auto response = QSharedPointer::create(); + ResponseApdu response; - CardReturnCode returnCode = mCardConnectionWorker->transmit(commandBuilder.build(), *response); - if (returnCode != CardReturnCode::OK || response->getReturnCode() != StatusCode::SUCCESS) + const CardReturnCode returnCode = mCardConnectionWorker->transmit(commandBuilder.build(), response); + if (returnCode != CardReturnCode::OK || response.getReturnCode() != StatusCode::SUCCESS) { - qCCritical(card) << "Error on GA(Mutual Authentication):" << getResponseErrorString(returnCode, response->getReturnCode()); + qCCritical(card) << "Error on GA(Mutual Authentication):" << getResponseErrorString(returnCode, response.getReturnCode()); } - return response; + return GAMutualAuthenticationResponse(response); } diff --git a/src/card/base/pace/KeyAgreement.h b/src/card/base/pace/KeyAgreement.h index d8aebc3..3ff1a45 100644 --- a/src/card/base/pace/KeyAgreement.h +++ b/src/card/base/pace/KeyAgreement.h @@ -7,7 +7,6 @@ #pragma once #include "CardConnectionWorker.h" -#include "CardOperationResult.h" #include "GeneralAuthenticateResponse.h" #include "pace/KeyDerivationFunction.h" @@ -27,19 +26,32 @@ enum class KeyAgreementStatus class KeyAgreement { + protected: + struct CardResult + { + CardReturnCode mReturnCode = CardReturnCode::UNDEFINED; + QByteArray mData = QByteArray(); + }; + private: const QSharedPointer mCardConnectionWorker; QByteArray mEncryptionKey; QByteArray mMacKey; QByteArray mCarCurr, mCarPrev; + + CardResult createTransmitResult(CardReturnCode pReturnCode, + StatusCode pResponseReturnCode, + const QByteArray& pResultData, + const char* pLogMessage); + /*! * \brief Determine the card's nonce. The encrypted nonce will be decrypted using the supplied PIN. * This represents the first step "General Authenticate" of TR-03110 Part 3, page 47. * \param pPin PIN for decryption of the nonce * \return the decrypted nonce */ - CardOperationResult determineNonce(const QString& pPin); + CardResult determineNonce(const QString& pPin); /*! * \brief Determines the shared secret by performing the key agreement. @@ -47,7 +59,7 @@ class KeyAgreement * \param pNonce the nonce needed for key agreement. * \return the shared secret between terminal and card */ - virtual CardOperationResult determineSharedSecret(const QByteArray& pNonce) = 0; + virtual CardResult determineSharedSecret(const QByteArray& pNonce) = 0; /*! * \brief Returns the uncompressed terminal's ephemeral public key calculated during key agreement. @@ -60,7 +72,7 @@ class KeyAgreement * \brief Transmit the General Authenticate (Encrypted Nonce) command to the card. * \return the encrypted nonce */ - CardOperationResult transmitGAEncryptedNonce(); + CardResult transmitGAEncryptedNonce(); /*! * \brief Performs the mutual authentication of terminal and card using the determined shared secret. @@ -80,21 +92,21 @@ class KeyAgreement * \param pMappingData the terminal's mapping data. * \return the card's mapping data */ - CardOperationResult transmitGAMappingData(const QByteArray& pMappingData); + CardResult transmitGAMappingData(const QByteArray& pMappingData); /*! * \brief Transmit the General Authenticate (Ephemeral Public Key) command to the card. * \param pEphemeralPublicKey the terminal's ephemeral public key * \return the card's ephemeral public key */ - CardOperationResult transmitGAEphemeralPublicKey(const QByteArray& pEphemeralPublicKey); + CardResult transmitGAEphemeralPublicKey(const QByteArray& pEphemeralPublicKey); /*! * \brief Transmit the General Authenticate (Mutual Authentication) command to the card. * \param pMutualAuthenticationData the terminal's authentication token * \return the complete response APDU */ - QSharedPointer transmitGAMutualAuthentication(const QByteArray& pMutualAuthenticationData); + GAMutualAuthenticationResponse transmitGAMutualAuthentication(const QByteArray& pMutualAuthenticationData); public: /*! diff --git a/src/card/base/pace/KeyDerivationFunction.h b/src/card/base/pace/KeyDerivationFunction.h index a6d2039..6e60456 100644 --- a/src/card/base/pace/KeyDerivationFunction.h +++ b/src/card/base/pace/KeyDerivationFunction.h @@ -26,7 +26,7 @@ class KeyDerivationFunction final * \param pPaceAlgorithm algorithm of PACE protocol. This will determine the key derivation algorithm to use. E.g. a * PACE protocol of id_PACE::DH::GM_AES_CBC_CMAC_128 will result in SHA256 to be used internally to derive keys. */ - KeyDerivationFunction(const QByteArray& pPaceAlgorithm); + explicit KeyDerivationFunction(const QByteArray& pPaceAlgorithm); ~KeyDerivationFunction() = default; /*! diff --git a/src/card/base/pace/PaceHandler.cpp b/src/card/base/pace/PaceHandler.cpp index 8e5c77d..1cccf6b 100644 --- a/src/card/base/pace/PaceHandler.cpp +++ b/src/card/base/pace/PaceHandler.cpp @@ -6,7 +6,7 @@ #include "asn1/KnownOIDs.h" #include "asn1/PaceInfo.h" -#include "Commands.h" +#include "MSEBuilder.h" #include "pace/ec/EllipticCurveFactory.h" #include "pace/KeyAgreement.h" #include "PersoSimWorkaround.h" @@ -105,7 +105,9 @@ CardReturnCode PaceHandler::establishPaceChannel(PacePasswordId pPasswordId, con qCDebug(card) << "Pace channel established"; return CardReturnCode::OK; } + Q_UNREACHABLE(); + return CardReturnCode::UNDEFINED; } diff --git a/src/card/base/pace/PaceHandler.h b/src/card/base/pace/PaceHandler.h index 1b55727..64f5ea2 100644 --- a/src/card/base/pace/PaceHandler.h +++ b/src/card/base/pace/PaceHandler.h @@ -55,7 +55,7 @@ class PaceHandler final Q_DISABLE_COPY(PaceHandler) public: - PaceHandler(const QSharedPointer& pCardConnectionWorker); + explicit PaceHandler(const QSharedPointer& pCardConnectionWorker); /*! * \brief Performs the PACE protocol and establishes a PACE channel. diff --git a/src/card/base/pace/SecureMessaging.cpp b/src/card/base/pace/SecureMessaging.cpp index f8b4c47..544dc5d 100644 --- a/src/card/base/pace/SecureMessaging.cpp +++ b/src/card/base/pace/SecureMessaging.cpp @@ -91,7 +91,7 @@ CommandApdu SecureMessaging::encrypt(const CommandApdu& pCommandApdu) if (!isInitialized()) { qCCritical(card) << "SecureMessaging not successfully initialized"; - return QByteArray(); + return CommandApdu(QByteArray()); } ++mSendSequenceCounter; @@ -199,12 +199,12 @@ QByteArray SecureMessaging::getEncryptedIv() } -bool SecureMessaging::decrypt(const ResponseApdu& pEncryptedResponseApdu, ResponseApdu& pDecryptedResponseApdu) +ResponseApdu SecureMessaging::decrypt(const ResponseApdu& pEncryptedResponseApdu) { if (!isInitialized()) { qCCritical(card) << "SecureMessaging not successfully initialized"; - return false; + return ResponseApdu(); } ++mSendSequenceCounter; @@ -212,12 +212,12 @@ bool SecureMessaging::decrypt(const ResponseApdu& pEncryptedResponseApdu, Respon SecureMessagingResponse secureResponse(pEncryptedResponseApdu.getBuffer()); if (secureResponse.isInvalid()) { - return false; + return ResponseApdu(); } if (secureResponse.getSecuredStatusCode() != secureResponse.getReturnCode()) { qCCritical(card) << "SW1SW2 on secured ResponseApdu does not match"; - return false; + return ResponseApdu(); } QByteArray dataToMac; @@ -231,7 +231,7 @@ bool SecureMessaging::decrypt(const ResponseApdu& pEncryptedResponseApdu, Respon if (mCipherMac.generate(dataToMac) != secureResponse.getMac()) { qCCritical(card) << "MAC on secured ResponseApdu does not match"; - return false; + return ResponseApdu(); } QByteArray decryptedData; @@ -242,9 +242,7 @@ bool SecureMessaging::decrypt(const ResponseApdu& pEncryptedResponseApdu, Respon decryptedData = unpadFromCipherBlockSize(paddedDecryptedData); } - pDecryptedResponseApdu.setBuffer(decryptedData + secureResponse.getSecuredStatusCodeBytes()); - - qCDebug(secure) << "Plain ResponseApdu:" << pDecryptedResponseApdu.getBuffer().toHex(); - - return true; + const ResponseApdu response(decryptedData + secureResponse.getSecuredStatusCodeBytes()); + qCDebug(secure) << "Plain ResponseApdu:" << response.getBuffer().toHex(); + return response; } diff --git a/src/card/base/pace/SecureMessaging.h b/src/card/base/pace/SecureMessaging.h index b61b4a7..affc28b 100644 --- a/src/card/base/pace/SecureMessaging.h +++ b/src/card/base/pace/SecureMessaging.h @@ -54,7 +54,13 @@ class SecureMessaging final bool isInitialized(); CommandApdu encrypt(const CommandApdu& pCommandApdu); - bool decrypt(const ResponseApdu& pEncryptedResponseApdu, ResponseApdu& pDecryptedResponseApdu); + + /*! + * \brief Decrypt given ResponseApdu. + * + * \return Decrypted ResponseApdu or an empty ResponseApdu if decryption fails. + */ + ResponseApdu decrypt(const ResponseApdu& pEncryptedResponseApdu); }; } // namespace governikus diff --git a/src/card/base/pace/ec/EcdhGenericMapping.h b/src/card/base/pace/ec/EcdhGenericMapping.h index 8ed1968..bff92ad 100644 --- a/src/card/base/pace/ec/EcdhGenericMapping.h +++ b/src/card/base/pace/ec/EcdhGenericMapping.h @@ -28,7 +28,7 @@ class EcdhGenericMapping void setGenerator(const QSharedPointer& pNewGenerator); public: - EcdhGenericMapping(const QSharedPointer& pCurve); + explicit EcdhGenericMapping(const QSharedPointer& pCurve); virtual ~EcdhGenericMapping() override = default; QByteArray generateTerminalMappingData() override; diff --git a/src/card/base/pace/ec/EcdhKeyAgreement.cpp b/src/card/base/pace/ec/EcdhKeyAgreement.cpp index 861e677..943c111 100644 --- a/src/card/base/pace/ec/EcdhKeyAgreement.cpp +++ b/src/card/base/pace/ec/EcdhKeyAgreement.cpp @@ -2,17 +2,16 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ +#include "pace/ec/EcdhKeyAgreement.h" + #include "asn1/KnownOIDs.h" #include "asn1/PaceInfo.h" -#include "Commands.h" #include "pace/ec/EcdhGenericMapping.h" -#include "pace/ec/EcdhKeyAgreement.h" #include "pace/ec/EcUtil.h" #include "pace/ec/EllipticCurveFactory.h" #include - using namespace governikus; @@ -51,7 +50,7 @@ EcdhKeyAgreement::EcdhKeyAgreement(const QSharedPointer& pPaceIn } -QSharedPointer EcdhKeyAgreement::create(const QSharedPointer& pPaceInfo, +QSharedPointer EcdhKeyAgreement::create(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker) { QSharedPointer keyAgreement(new EcdhKeyAgreement(pPaceInfo, pCardConnectionWorker)); @@ -74,93 +73,87 @@ QSharedPointer EcdhKeyAgreement::create(const QSharedPointer EcdhKeyAgreement::determineSharedSecret(const QByteArray& pNonce) +KeyAgreement::CardResult EcdhKeyAgreement::determineSharedSecret(const QByteArray& pNonce) { - CardOperationResult > ephemeralCurveResult = determineEphemeralDomainParameters(pNonce); - CardReturnCode ephemeralCurveResultCode = ephemeralCurveResult.getReturnCode(); - mEphemeralCurve = ephemeralCurveResult.getPayload(); + auto [ephemeralCurveResultCode, ephemeralCurve] = determineEphemeralDomainParameters(pNonce); + mEphemeralCurve = ephemeralCurve; if (ephemeralCurveResultCode != CardReturnCode::OK) { - return CardOperationResult(ephemeralCurveResultCode, QByteArray()); + return {ephemeralCurveResultCode}; } - CardOperationResult > mutualPointResult = performKeyExchange(mEphemeralCurve); - CardReturnCode mutualPointResultCode = mutualPointResult.getReturnCode(); - QSharedPointer mutualPoint = mutualPointResult.getPayload(); + auto [mutualPointResultCode, mutualPoint] = performKeyExchange(mEphemeralCurve); if (mutualPointResultCode != CardReturnCode::OK) { - return CardOperationResult(mutualPointResultCode, QByteArray()); + return {mutualPointResultCode}; } QByteArray sharedSecret = EcUtil::point2oct(mEphemeralCurve, mutualPoint.data()); sharedSecret = sharedSecret.mid(1, (sharedSecret.size() - 1) / 2); - return CardOperationResult(CardReturnCode::OK, sharedSecret); + return {CardReturnCode::OK, sharedSecret}; } -CardOperationResult > EcdhKeyAgreement::determineEphemeralDomainParameters(const QByteArray& pNonce) +QPair > EcdhKeyAgreement::determineEphemeralDomainParameters(const QByteArray& pNonce) { QByteArray terminalMappingData = mMapping->generateTerminalMappingData(); - CardOperationResult result = transmitGAMappingData(terminalMappingData); - CardReturnCode resultCode = result.getReturnCode(); + auto [resultCode, cardMappingData] = transmitGAMappingData(terminalMappingData); if (resultCode != CardReturnCode::OK) { - return CardOperationResult >(resultCode, QSharedPointer()); + return qMakePair(resultCode, QSharedPointer()); } - QByteArray cardMappingData = result.getPayload(); - return CardOperationResult >(CardReturnCode::OK, mMapping->generateEphemeralDomainParameters(cardMappingData, pNonce)); + return qMakePair(CardReturnCode::OK, mMapping->generateEphemeralDomainParameters(cardMappingData, pNonce)); } -CardOperationResult > EcdhKeyAgreement::performKeyExchange(const QSharedPointer& pCurve) +QPair > EcdhKeyAgreement::performKeyExchange(const QSharedPointer& pCurve) { QSharedPointer terminalEphemeralKey = EcUtil::create(EC_KEY_new()); if (!EC_KEY_set_group(terminalEphemeralKey.data(), pCurve.data())) { qCCritical(card) << "Error EC_KEY_set_group"; - return CardOperationResult >(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); + return qMakePair(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); } if (!EC_KEY_generate_key(terminalEphemeralKey.data())) { qCCritical(card) << "Error EC_KEY_generate_key"; - return CardOperationResult >(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); + return qMakePair(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); } // Make a copy of the terminal public key for later mutual authentication. mTerminalPublicKey = EcUtil::create(EC_POINT_dup(EC_KEY_get0_public_key(terminalEphemeralKey.data()), pCurve.data())); QByteArray terminalEphemeralPublicKeyBytes = EcUtil::point2oct(pCurve, mTerminalPublicKey.data()); - const BIGNUM* terminalPrivateKey = EC_KEY_get0_private_key(terminalEphemeralKey.data()); + const BIGNUM* const terminalPrivateKey = EC_KEY_get0_private_key(terminalEphemeralKey.data()); - CardOperationResult result = transmitGAEphemeralPublicKey(terminalEphemeralPublicKeyBytes); - CardReturnCode resultCode = result.getReturnCode(); + auto [resultCode, cardEphemeralPublicKeyBytes] = transmitGAEphemeralPublicKey(terminalEphemeralPublicKeyBytes); if (resultCode != CardReturnCode::OK) { - return CardOperationResult >(resultCode, QSharedPointer()); + return qMakePair(resultCode, QSharedPointer()); } - QByteArray cardEphemeralPublicKeyBytes = result.getPayload(); qCDebug(secure) << "uncompressedCardEphemeralPublicKey:" << cardEphemeralPublicKeyBytes.toHex(); mCardPublicKey = EcUtil::oct2point(pCurve, cardEphemeralPublicKeyBytes); if (!mCardPublicKey) { qCCritical(card) << "Cannot encode card ephemeral public key"; - return CardOperationResult >(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); + return qMakePair(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); } if (!EC_POINT_cmp(pCurve.data(), mTerminalPublicKey.data(), mCardPublicKey.data(), nullptr)) { qCCritical(card) << "The exchanged public keys are equal"; - return CardOperationResult >(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); + return qMakePair(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); } QSharedPointer mutualPoint = EcUtil::create(EC_POINT_new(pCurve.data())); if (!EC_POINT_mul(pCurve.data(), mutualPoint.data(), nullptr, mCardPublicKey.data(), terminalPrivateKey, nullptr)) { qCCritical(card) << "Calculation of elliptic curve point H failed"; - return CardOperationResult >(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); + return qMakePair(CardReturnCode::PROTOCOL_ERROR, QSharedPointer()); } - return CardOperationResult >(CardReturnCode::OK, mutualPoint); + + return qMakePair(CardReturnCode::OK, mutualPoint); } diff --git a/src/card/base/pace/ec/EcdhKeyAgreement.h b/src/card/base/pace/ec/EcdhKeyAgreement.h index 533f8f6..23d289c 100644 --- a/src/card/base/pace/ec/EcdhKeyAgreement.h +++ b/src/card/base/pace/ec/EcdhKeyAgreement.h @@ -10,6 +10,7 @@ #include "pace/KeyAgreement.h" #include +#include #include class test_EcdhKeyAgreement; @@ -28,13 +29,13 @@ class EcdhKeyAgreement QSharedPointer mTerminalPublicKey; QSharedPointer mCardPublicKey; - CardOperationResult > determineEphemeralDomainParameters(const QByteArray& pNonce); - CardOperationResult > performKeyExchange(const QSharedPointer& pCurve); + QPair > determineEphemeralDomainParameters(const QByteArray& pNonce); + QPair > performKeyExchange(const QSharedPointer& pCurve); static QByteArray encodeUncompressedPublicKey(const QSharedPointer& pPaceInfo, const QSharedPointer& pCurve, const QSharedPointer& pPoint); static QByteArray encodeCompressedPublicKey(const QSharedPointer& pCurve, const QSharedPointer& pPoint); - CardOperationResult determineSharedSecret(const QByteArray& pNonce) override; + KeyAgreement::CardResult determineSharedSecret(const QByteArray& pNonce) override; QByteArray getUncompressedTerminalPublicKey() override; QByteArray getUncompressedCardPublicKey() override; QByteArray getCompressedCardPublicKey() override; @@ -42,7 +43,7 @@ class EcdhKeyAgreement EcdhKeyAgreement(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker); public: - static QSharedPointer create(const QSharedPointer& pPaceInfo, + static QSharedPointer create(const QSharedPointer& pPaceInfo, const QSharedPointer& pCardConnectionWorker); virtual ~EcdhKeyAgreement() override = default; diff --git a/src/card/bluetooth/BluetoothCard.cpp b/src/card/bluetooth/BluetoothCard.cpp index 778ef61..0adf99f 100644 --- a/src/card/bluetooth/BluetoothCard.cpp +++ b/src/card/bluetooth/BluetoothCard.cpp @@ -121,10 +121,9 @@ CardReturnCode BluetoothCard::transmit(const CommandApdu& pCmd, ResponseApdu& pR } -CardReturnCode BluetoothCard::establishPaceChannel(PacePasswordId pPasswordId, +EstablishPaceChannelOutput BluetoothCard::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, - EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) { EstablishPaceChannel builder; @@ -140,8 +139,9 @@ CardReturnCode BluetoothCard::establishPaceChannel(PacePasswordId pPasswordId, return returnCode; } - pChannelOutput.parseFromCcid(response.getBuffer(), pPasswordId); - return pChannelOutput.getPaceReturnCode(); + EstablishPaceChannelOutput output; + output.parseFromCcid(response.getBuffer(), pPasswordId); + return output; } diff --git a/src/card/bluetooth/BluetoothCard.h b/src/card/bluetooth/BluetoothCard.h index 8be668c..1107c1c 100644 --- a/src/card/bluetooth/BluetoothCard.h +++ b/src/card/bluetooth/BluetoothCard.h @@ -26,7 +26,7 @@ class BluetoothCard CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes, quint8 pTimeoutSeconds); public: - BluetoothCard(QSharedPointer pDevice); + explicit BluetoothCard(QSharedPointer pDevice); CardReturnCode connect() override; CardReturnCode disconnect() override; @@ -34,7 +34,7 @@ class BluetoothCard CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; - CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) override; + EstablishPaceChannelOutput establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, quint8 pTimeoutSeconds) override; CardReturnCode destroyPaceChannel() override; diff --git a/src/card/bluetooth/BluetoothReader.cpp b/src/card/bluetooth/BluetoothReader.cpp index 6bf48cd..445d331 100644 --- a/src/card/bluetooth/BluetoothReader.cpp +++ b/src/card/bluetooth/BluetoothReader.cpp @@ -39,7 +39,12 @@ BluetoothReader::BluetoothReader(const QSharedPointer& pDev Card* BluetoothReader::getCard() const { - return mCard.data(); + if (mCard) + { + return mCard.data(); + } + + return nullptr; } @@ -83,8 +88,9 @@ void BluetoothReader::onInitialized(const QBluetoothDeviceInfo&) } -void BluetoothReader::disconnectReader() +void BluetoothReader::disconnectReader(const QString& pError) { + Q_UNUSED(pError); mDevice->disconnectFromDevice(); } @@ -110,7 +116,7 @@ void BluetoothReader::onError(QLowEnergyController::Error pError) { if (pError == QLowEnergyController::ConnectionError) { - Q_EMIT fireReaderDeviceError(GlobalStatus::Code::Workflow_Reader_Device_Connection_Error); + Q_EMIT fireReaderDeviceError(GlobalStatus::Code::Workflow_Bluetooth_Reader_Connection_Error); } } @@ -128,14 +134,14 @@ void BluetoothReader::onStatusCharacteristicChanged(const QByteArray& pValue) auto messages = BluetoothMessageParser(pValue).getMessages(); if (messages.size() != 1 || messages.at(0)->getBluetoothMsgId() != BluetoothMsgId::StatusInd) { - qCCritical(card) << "Cannot handle Bluetooth message"; + qCCritical(bluetooth) << "Cannot handle Bluetooth message"; return; } auto statusChange = messages.at(0).staticCast()->getStatusChange(); if (mCard.isNull() && (statusChange == BluetoothStatusChange::CardInserted || statusChange == BluetoothStatusChange::CardReset)) { - qCDebug(card) << "Card inserted" << getName(); + qCDebug(bluetooth) << "Card inserted" << getName(); mCard.reset(new BluetoothCard(mDevice)); QSharedPointer cardConnection = createCardConnectionWorker(); CardInfoFactory::create(cardConnection, mReaderInfo); @@ -147,15 +153,15 @@ void BluetoothReader::onStatusCharacteristicChanged(const QByteArray& pValue) } else { - qCWarning(card) << "Got unhandled card reader status" << statusChange; + qCWarning(bluetooth) << "Got unhandled card reader status" << statusChange; } } void BluetoothReader::onCardRemoved() { - qCDebug(card) << "Card removed" << getName(); + qCDebug(bluetooth) << "Card removed" << getName(); mLastCardEvent = CardEvent::CARD_REMOVED; - mReaderInfo.setCardInfo(CardInfo(CardType::NONE)); mCard.reset(); + mReaderInfo.setCardInfo(CardInfo(CardType::NONE)); } diff --git a/src/card/bluetooth/BluetoothReader.h b/src/card/bluetooth/BluetoothReader.h index 9deba58..a6c34d6 100644 --- a/src/card/bluetooth/BluetoothReader.h +++ b/src/card/bluetooth/BluetoothReader.h @@ -41,12 +41,12 @@ class BluetoothReader void fireReaderConnectionFailed(const QString& pReaderName); public: - BluetoothReader(const QSharedPointer& pDevice); + explicit BluetoothReader(const QSharedPointer& pDevice); Card* getCard() const override; void connectReader() override; - void disconnectReader() override; + void disconnectReader(const QString& pError = QString()) override; }; } // namespace governikus diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn.cpp b/src/card/bluetooth/BluetoothReaderManagerPlugIn.cpp index 2bf5f1b..cbee29a 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn.cpp +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn.cpp @@ -10,7 +10,12 @@ #include "BluetoothReader.h" #include "BluetoothReaderManagerPlugIn_p.h" #include "GlobalStatus.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include +#else #include "ScopeGuard.h" +#endif #include #include @@ -86,7 +91,7 @@ void BluetoothReaderManagerPlugIn::startScan(bool /*pAutoConnect*/) void BluetoothReaderManagerPlugIn::setBluetoothStatus(bool pEnabled) { - setReaderInfoEnabled(pEnabled); + setPlugInEnabled(pEnabled); if (!pEnabled) { while (!mReaders.isEmpty()) @@ -97,8 +102,10 @@ void BluetoothReaderManagerPlugIn::setBluetoothStatus(bool pEnabled) } -void BluetoothReaderManagerPlugIn::stopScan() +void BluetoothReaderManagerPlugIn::stopScan(const QString& pError) { + Q_UNUSED(pError); + if (mDeviceDiscoveryAgent.isActive()) { qCDebug(bluetooth) << "Stopping Bluetooth device discovery"; @@ -167,7 +174,7 @@ QVector BluetoothReaderManagerPlugIn::deviceIdsForReaderName(const QStr for (QMap::const_iterator it = mReaders.constBegin(); it != mReaders.constEnd(); ++it) { const QString& deviceId = it.key(); - const Reader* reader = it.value(); + const Reader* const reader = it.value(); if (reader->getName() == pReaderName) { result += deviceId; @@ -180,7 +187,7 @@ QVector BluetoothReaderManagerPlugIn::deviceIdsForReaderName(const QStr void BluetoothReaderManagerPlugIn::onDeviceDiscovered(const QBluetoothDeviceInfo& pInfo) { - setReaderInfoResponding(true); + setPlugInResponding(true); QString deviceId = BluetoothDeviceUtil::getDeviceId(pInfo); if (mReaders.contains(deviceId)) { @@ -218,7 +225,7 @@ void BluetoothReaderManagerPlugIn::onDeviceInitialized(const QBluetoothDeviceInf } disconnect(device.data(), &CyberJackWaveDevice::fireInitialized, this, &BluetoothReaderManagerPlugIn::onDeviceInitialized); - const ScopeGuard disconnector([device] { + const auto disconnector = qScopeGuard([device] { device->disconnectFromDevice(); }); @@ -229,7 +236,7 @@ void BluetoothReaderManagerPlugIn::onDeviceInitialized(const QBluetoothDeviceInf Q_ASSERT_X(!mReaders.contains(deviceId), "BluetoothReaderManagerPlugIn", "Device is already determined as reader"); - BluetoothReader* reader = new BluetoothReader(device); + auto* reader = new BluetoothReader(device); qCDebug(bluetooth) << "Device is successfully initialized, create reader" << reader->getName(); connect(reader, &BluetoothReader::fireReaderConnected, this, &BluetoothReaderManagerPlugIn::onReaderConnected); @@ -299,7 +306,7 @@ void BluetoothReaderManagerPlugIn::onDeviceDiscoveryError(QBluetoothDeviceDiscov if (pError == QBluetoothDeviceDiscoveryAgent::InputOutputError) { - setReaderInfoResponding(false); + setPlugInResponding(false); } Q_EMIT fireReaderDeviceError(pError == QBluetoothDeviceDiscoveryAgent::PoweredOffError ? GlobalStatus::Code::No_Error : GlobalStatus::Code::Workflow_Reader_Device_Scan_Error); diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn.h b/src/card/bluetooth/BluetoothReaderManagerPlugIn.h index 3f8256d..d643cc3 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn.h +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn.h @@ -63,7 +63,7 @@ class BluetoothReaderManagerPlugIn QList getReaders() const override; virtual void startScan(bool pAutoConnect) override; - virtual void stopScan() override; + virtual void stopScan(const QString& pError = QString()) override; }; } // namespace governikus diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p.h b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p.h index ec6c011..94252a8 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p.h +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p.h @@ -18,7 +18,7 @@ class BluetoothReaderManagerPlugInPrivate Q_DECLARE_PUBLIC(BluetoothReaderManagerPlugIn) BluetoothReaderManagerPlugIn* const q_ptr; - BluetoothReaderManagerPlugInPrivate(BluetoothReaderManagerPlugIn* pPublic); + explicit BluetoothReaderManagerPlugInPrivate(BluetoothReaderManagerPlugIn* pPublic); public Q_SLOTS: void onBluetoothStatusChanged(bool pEnabled); diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_android.cpp b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_android.cpp index 32abbcd..f41b3be 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_android.cpp +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_android.cpp @@ -67,7 +67,7 @@ void BluetoothReaderManagerPlugInPrivate::init() { Q_Q(BluetoothReaderManagerPlugIn); const auto& adapter = AndroidBluetoothAdapter::getDefaultAdapter(); - q->setReaderInfoAvailable(adapter.isAvailable()); + q->setPlugInAvailable(adapter.isAvailable()); q->setBluetoothStatus(adapter.isStateOn()); } diff --git a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_ios.mm b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_ios.mm index 556ba59..1cd83d6 100644 --- a/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_ios.mm +++ b/src/card/bluetooth/BluetoothReaderManagerPlugIn_p_ios.mm @@ -22,7 +22,7 @@ BluetoothReaderManagerPlugInPrivate::BluetoothReaderManagerPlugInPrivate(Bluetoo void BluetoothReaderManagerPlugInPrivate::init() { Q_Q(BluetoothReaderManagerPlugIn); - q->setReaderInfoAvailable(true); + q->setPlugInAvailable(true); q->setBluetoothStatus(true); } @@ -35,7 +35,7 @@ void BluetoothReaderManagerPlugInPrivate::onBluetoothStatusChanged(bool) void BluetoothReaderManagerPlugInPrivate::onScanStart() { NSMutableDictionary* options = [[[NSMutableDictionary alloc] init] autorelease]; - [options setObject:[NSNumber numberWithBool:YES] forKey:CBCentralManagerOptionShowPowerAlertKey]; + options[CBCentralManagerOptionShowPowerAlertKey] = @YES; // Display a dialog to the user if Bluetooth is off [[[CBCentralManager alloc] initWithDelegate:nil queue:nil options:options] autorelease]; } diff --git a/src/card/bluetooth/CyberJackWaveDevice.cpp b/src/card/bluetooth/CyberJackWaveDevice.cpp index 8af5bb5..adcfe9e 100644 --- a/src/card/bluetooth/CyberJackWaveDevice.cpp +++ b/src/card/bluetooth/CyberJackWaveDevice.cpp @@ -248,7 +248,7 @@ QLowEnergyService::WriteMode CyberJackWaveDevice::determineWriteMode(int pBlockI return (pBlockIndex % IOS_CONFIRMATION_BLOCK_NUMBER) ? QLowEnergyService::WriteWithoutResponse : QLowEnergyService::WriteWithResponse; #else - Q_UNUSED(pBlockIndex); + Q_UNUSED(pBlockIndex) /* * According to Reiner SCT on Android the behaviour is too device specific, diff --git a/src/card/bluetooth/messages/BluetoothMessageParser.cpp b/src/card/bluetooth/messages/BluetoothMessageParser.cpp index 3b97dff..15283fe 100644 --- a/src/card/bluetooth/messages/BluetoothMessageParser.cpp +++ b/src/card/bluetooth/messages/BluetoothMessageParser.cpp @@ -20,15 +20,10 @@ #include "parameter/BluetoothMessageParameterResultCode.h" #include "parameter/BluetoothMessageParameterStatusChange.h" -#include - using namespace governikus; -Q_DECLARE_LOGGING_CATEGORY(card) - - BluetoothMessageParser::BluetoothMessageParser(const QByteArray& pData) : mMessages() , mRemainingBytes(pData) @@ -46,7 +41,7 @@ void BluetoothMessageParser::parse() { while (mRemainingBytes.size() >= 4) { - BluetoothMsgId msgId = static_cast(mRemainingBytes.at(0)); + auto msgId = static_cast(mRemainingBytes.at(0)); int parameterCount = mRemainingBytes.at(1); auto message = createMessage(msgId); @@ -70,7 +65,7 @@ bool BluetoothMessageParser::parseParameter(QSharedPointer pMe { if (data.size() >= 4) { - BluetoothParamId paramId = static_cast(data.at(0)); + auto paramId = static_cast(data.at(0)); ushort paramLength = getParamLength(static_cast(data.at(2)), static_cast(data.at(3))); data = data.mid(4); diff --git a/src/card/bluetooth/messages/BluetoothUtils.cpp b/src/card/bluetooth/messages/BluetoothUtils.cpp index d695afb..104f726 100644 --- a/src/card/bluetooth/messages/BluetoothUtils.cpp +++ b/src/card/bluetooth/messages/BluetoothUtils.cpp @@ -14,7 +14,7 @@ Q_DECLARE_LOGGING_CATEGORY(bluetooth) ushort BluetoothUtils::getPaddingLength(ushort pParamLen, ushort pPaddingLen) { - ushort needsPaddingLen = static_cast(pPaddingLen - (pParamLen % pPaddingLen)); + auto needsPaddingLen = static_cast(pPaddingLen - (pParamLen % pPaddingLen)); return needsPaddingLen == pPaddingLen ? 0 : needsPaddingLen; } diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.cpp b/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.cpp index 99c2111..26a142a 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.cpp +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.cpp @@ -3,13 +3,11 @@ */ #include "BluetoothMessageParameter.h" -#include "messages/BluetoothUtils.h" -#include +#include "messages/BluetoothUtils.h" using namespace governikus; -Q_DECLARE_LOGGING_CATEGORY(bluetooth) QDebug operator<<(QDebug pDbg, const BluetoothMessageParameter& pMsg) { diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.h b/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.h index 531690c..cdf48d3 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.h +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameter.h @@ -7,9 +7,11 @@ #pragma once #include "messages/BluetoothIDs.h" + #include -Q_DECLARE_LOGGING_CATEGORY(card) +Q_DECLARE_LOGGING_CATEGORY(bluetooth) + namespace governikus { @@ -36,13 +38,13 @@ class BluetoothMessageParameter pDest = static_cast(pValue.at(0)); if (!Enum::isValue(pValue.at(0))) { - qCWarning(card) << "Value is unknown:" << pDest; + qCWarning(bluetooth) << "Value is unknown:" << pDest; } return true; } else { - qCWarning(card) << "Content has wrong size:" << pValue.toHex(); + qCWarning(bluetooth) << "Content has wrong size:" << pValue.toHex(); } return false; diff --git a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterMaxMsgSize.cpp b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterMaxMsgSize.cpp index d67fd9f..b155ff7 100644 --- a/src/card/bluetooth/messages/parameter/BluetoothMessageParameterMaxMsgSize.cpp +++ b/src/card/bluetooth/messages/parameter/BluetoothMessageParameterMaxMsgSize.cpp @@ -12,7 +12,7 @@ BluetoothMessageParameterMaxMsgSize::BluetoothMessageParameterMaxMsgSize(const Q { if (pValue.size() != 2) { - qCWarning(card) << "Content has wrong length"; + qCWarning(bluetooth) << "Content has wrong length"; mValue.clear(); mValid = false; return; @@ -27,7 +27,7 @@ BluetoothMessageParameterMaxMsgSize::BluetoothMessageParameterMaxMsgSize(uint pM { if (pMaxMsgSize >> 16) { - qCWarning(card) << "MaxMsgSize has wrong length"; + qCWarning(bluetooth) << "MaxMsgSize has wrong length"; mValid = false; return; } diff --git a/src/card/drivers/ReaderDetector.h b/src/card/drivers/ReaderDetector.h index 2795d00..c91de23 100644 --- a/src/card/drivers/ReaderDetector.h +++ b/src/card/drivers/ReaderDetector.h @@ -55,7 +55,7 @@ class ReaderDetector protected: ReaderDetector(); - virtual ~ReaderDetector(); + ~ReaderDetector() override; static ReaderDetector& getInstance(); public: diff --git a/src/card/drivers/ReaderDetector_linux.cpp b/src/card/drivers/ReaderDetector_linux.cpp index 9f85f0e..9436378 100644 --- a/src/card/drivers/ReaderDetector_linux.cpp +++ b/src/card/drivers/ReaderDetector_linux.cpp @@ -152,7 +152,7 @@ QVector ReaderDetector::attachedDevIds() const /* Create the udev object */ udev = udev_new(); - if (!udev) + if (udev == nullptr) { qCDebug(card_drivers) << "Can't create udev"; return result; @@ -173,7 +173,7 @@ QVector ReaderDetector::attachedDevIds() const { /* Get the filename of the /sys entry for the device and create a udev_device object (dev) representing it */ - const char* path = udev_list_entry_get_name(dev_list_entry); + const char* const path = udev_list_entry_get_name(dev_list_entry); struct udev_device* dev = udev_device_new_from_syspath(udev, path); /* The device pointed to by dev contains information about @@ -183,7 +183,7 @@ QVector ReaderDetector::attachedDevIds() const be several levels up the tree, but the function will find it.*/ dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"); - if (!dev) + if (dev == nullptr) { continue; } diff --git a/src/card/drivers/ReaderDetector_osx.cpp b/src/card/drivers/ReaderDetector_osx.cpp index d5908bb..213c16f 100644 --- a/src/card/drivers/ReaderDetector_osx.cpp +++ b/src/card/drivers/ReaderDetector_osx.cpp @@ -11,12 +11,11 @@ #include +Q_DECLARE_LOGGING_CATEGORY(card_drivers) using namespace governikus; -Q_DECLARE_LOGGING_CATEGORY(card_drivers) - #define VENDOR_ID "idVendor" #define PRODUCT_ID "idProduct" @@ -28,7 +27,7 @@ static void deviceChanged(void* refCon, io_iterator_t iterator) // Clear the recognized changes } - qDebug() << "System information: device changed"; + qCDebug(card_drivers) << "System information: device changed"; ReaderDetector* readerDetector = static_cast(refCon); Q_EMIT readerDetector->fireReaderChangeDetected(); @@ -53,7 +52,7 @@ static bool listenTo(const io_name_t notificationType, ReaderDetector* readerDet if (kr != KERN_SUCCESS) { - qDebug() << "IOServiceAddMatchingNotification returned" << kr; + qCDebug(card_drivers) << "IOServiceAddMatchingNotification returned" << kr; return false; } diff --git a/src/card/drivers/ReaderDetector_win.cpp b/src/card/drivers/ReaderDetector_win.cpp index dd47b57..a2577c3 100644 --- a/src/card/drivers/ReaderDetector_win.cpp +++ b/src/card/drivers/ReaderDetector_win.cpp @@ -17,11 +17,9 @@ #include -using namespace governikus; - - Q_DECLARE_LOGGING_CATEGORY(card_drivers) +using namespace governikus; bool ReaderDetector::initNativeEvents() { @@ -131,7 +129,7 @@ QVector ReaderDetector::attachedDevIds() const bool ReaderDetector::nativeEventFilter(const QByteArray& pEventType, void* pMessage, long* pResult) { - Q_UNUSED(pResult); + Q_UNUSED(pResult) if (pEventType == "windows_generic_MSG") { @@ -140,7 +138,7 @@ bool ReaderDetector::nativeEventFilter(const QByteArray& pEventType, void* pMess { if (msg->wParam == DBT_DEVNODES_CHANGED) { - qDebug() << "System information: device changed"; + qCDebug(card_drivers) << "System information: device changed"; Q_EMIT fireReaderChangeDetected(); } } diff --git a/src/card/ios/CMakeLists.txt b/src/card/ios/CMakeLists.txt new file mode 100644 index 0000000..821afcb --- /dev/null +++ b/src/card/ios/CMakeLists.txt @@ -0,0 +1,8 @@ +##################################################################### +# The ReaderManagerPlugin for iOS Nfc. +##################################################################### + +ADD_PLATFORM_LIBRARY(AusweisAppCardIos) + +TARGET_LINK_LIBRARIES(AusweisAppCardIos Qt5::Core AusweisAppGlobal AusweisAppCard) +TARGET_COMPILE_DEFINITIONS(AusweisAppCardIos PRIVATE QT_STATICPLUGIN) diff --git a/src/card/ios/IosCard.h b/src/card/ios/IosCard.h new file mode 100644 index 0000000..7390030 --- /dev/null +++ b/src/card/ios/IosCard.h @@ -0,0 +1,49 @@ +/*! + * \brief Implementation of \ref Card for iOS. + * + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "Card.h" + +#include +#include + + +namespace governikus +{ +struct IosCardPointer; + +class IosCard + : public Card +{ + Q_OBJECT + + private: + IosCardPointer* const mCard; + bool mConnected; + QWaitCondition mWaitCondition; + QMutex mCallbackDone; + + public: + explicit IosCard(IosCardPointer* pTag); + virtual ~IosCard() override; + + bool isValid() const; + bool invalidateTarget(); + + virtual CardReturnCode connect() override; + virtual CardReturnCode disconnect() override; + virtual bool isConnected() override; + virtual void setProgressMessage(const QString& pMessage) override; + + virtual CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; + + Q_SIGNALS: + void fireConnectFailed(); + void fireTransmitFailed(); +}; + +} // namespace governikus diff --git a/src/card/ios/IosCard.mm b/src/card/ios/IosCard.mm new file mode 100644 index 0000000..bae283c --- /dev/null +++ b/src/card/ios/IosCard.mm @@ -0,0 +1,182 @@ +/*! + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "IosCard.h" + +#include "IosCardPointer.h" + +#include + +#import +#import +#import + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(card_nfc) + + +IosCard::IosCard(IosCardPointer* const pCard) + : Card() + , mCard(pCard) + , mConnected(false) + , mWaitCondition() + , mCallbackDone() +{ + qCDebug(card_nfc) << "Card created"; + + [mCard->mNfcTag retain]; + mCallbackDone.lock(); +} + + +IosCard::~IosCard() +{ + [mCard->mNfcTag release]; + delete mCard; +} + + +bool IosCard::isValid() const +{ + return mCard->mNfcTag && (!mConnected || [mCard->mNfcTag isAvailable]); +} + + +bool IosCard::invalidateTarget() +{ + if (isValid()) + { + [mCard->mNfcTag release]; + mCard->mNfcTag = nil; + return true; + } + + return false; +} + + +CardReturnCode IosCard::connect() +{ + if (!isValid()) + { + qCWarning(card_nfc) << "NearFieldTarget is no longer valid"; + return CardReturnCode::COMMAND_FAILED; + } + + if (isConnected()) + { + qCCritical(card_nfc) << "Card is already connected"; + return CardReturnCode::OK; + } + + NFCTagReaderSession* session = [mCard->mNfcTag session]; + [session connectToTag: mCard->mNfcTag completionHandler: ^(NSError* error){ + if (error != nil) + { + qCDebug(card_nfc) << "Error during connect:" << error; + } + else + { + mConnected = true; + } + + mWaitCondition.wakeOne(); + }]; + + mWaitCondition.wait(&mCallbackDone, 500); + if (!mConnected) + { + Q_EMIT fireTransmitFailed(); + return CardReturnCode::COMMAND_FAILED; + } + + return CardReturnCode::OK; +} + + +CardReturnCode IosCard::disconnect() +{ + if (!isValid()) + { + qCWarning(card_nfc) << "NearFieldTarget is no longer valid"; + return CardReturnCode::COMMAND_FAILED; + } + + if (!isConnected()) + { + qCCritical(card_nfc) << "Card is already disconnected"; + return CardReturnCode::COMMAND_FAILED; + } + + mConnected = false; + return CardReturnCode::OK; +} + + +bool IosCard::isConnected() +{ + return mConnected; +} + + +void IosCard::setProgressMessage(const QString& pMessage) +{ + NFCTagReaderSession* session = [mCard->mNfcTag session]; + [session setAlertMessage: pMessage.toNSString()]; +} + + +CardReturnCode IosCard::transmit(const CommandApdu& pCmd, ResponseApdu& pRes) +{ + if (!isValid()) + { + qCWarning(card_nfc) << "NearFieldTarget is no longer valid"; + return CardReturnCode::COMMAND_FAILED; + } + + qCDebug(card_nfc) << "Transmit command APDU:" << pCmd.getBuffer().toHex(); + + const auto resultBuffer = QSharedPointer::create(); // Don't use this inside of the Block + const QWeakPointer weakBuffer = resultBuffer; + + Q_ASSERT([mCard->mNfcTag conformsToProtocol:@protocol(NFCISO7816Tag)]); + const auto tag = static_cast >(mCard->mNfcTag); + auto* apdu = [[NFCISO7816APDU alloc] initWithData: pCmd.getBuffer().toNSData()]; + [tag sendCommandAPDU: apdu completionHandler: ^(NSData* responseData, uint8_t sw1, uint8_t sw2, NSError* error){ + // By referencing weakBuffer here, it will be copied into the Block. If the handler outlives the caller, resultBuffer won't exist anymore. + if (const auto recvBuffer = weakBuffer.lock()) + { + if (error == nil) + { + *recvBuffer = QByteArray::fromNSData(responseData); + *recvBuffer += static_cast(sw1); + *recvBuffer += static_cast(sw2); + qCDebug(card_nfc) << "Transmit response APDU:" << recvBuffer->toHex(); + } + else + { + qCDebug(card_nfc) << "Error during transmit:" << error; + } + + mWaitCondition.wakeOne(); + } + else + { + qCDebug(card_nfc) << "Caller doesn't exist anymore."; + } + }]; + + mWaitCondition.wait(&mCallbackDone, 500); + if (resultBuffer->isEmpty()) + { + Q_EMIT fireTransmitFailed(); + return CardReturnCode::COMMAND_FAILED; + } + + pRes.setBuffer(std::move(*resultBuffer)); + return CardReturnCode::OK; +} diff --git a/src/card/ios/IosCardPointer.h b/src/card/ios/IosCardPointer.h new file mode 100644 index 0000000..98c4ebe --- /dev/null +++ b/src/card/ios/IosCardPointer.h @@ -0,0 +1,16 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#import + +namespace governikus +{ + +/// Implementation detail which can only be used by Object-C++ since it stores a pointer to an Objective-C protocol. +struct IosCardPointer +{ + id mNfcTag; +}; + +} // namespace governikus diff --git a/src/card/ios/IosReader.h b/src/card/ios/IosReader.h new file mode 100644 index 0000000..9d9091d --- /dev/null +++ b/src/card/ios/IosReader.h @@ -0,0 +1,52 @@ +/*! + * \brief Implementation of \ref Reader for iOS. + * + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "IosCard.h" +#include "IosReaderDelegate.h" +#include "Reader.h" + +#include +#include + + +namespace governikus +{ + +class IosReader + : public ConnectableReader +{ + Q_OBJECT + + private: + IosReaderDelegate mDelegate; + QScopedPointer mCard; + bool mConnected; + qint64 mLastRestart; + + virtual CardEvent updateCard() override; + + void removeCard(); + void stopSession(const QString& pError = QString()); + + public: + IosReader(); + virtual ~IosReader() override; + + virtual Card* getCard() const override; + + virtual void connectReader() override; + virtual void disconnectReader(const QString& pError = QString()) override; + + private Q_SLOTS: + void onDiscoveredTag(IosCard* pCard); + void onDidInvalidateWithError(const QString& pError, bool pDoRestart); + void onConnectFailed(); + void onTransmitFailed(); +}; + +} // namespace governikus diff --git a/src/card/ios/IosReader.mm b/src/card/ios/IosReader.mm new file mode 100644 index 0000000..a99a97d --- /dev/null +++ b/src/card/ios/IosReader.mm @@ -0,0 +1,166 @@ +/*! + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "CardConnectionWorker.h" +#include "IosReader.h" + +#include +#include +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(card_nfc) + + +Reader::CardEvent IosReader::updateCard() +{ + if (mCard && !mCard->isValid()) + { + removeCard(); + mDelegate.startSession(); + return CardEvent::CARD_REMOVED; + } + + return CardEvent::NONE; +} + + +IosReader::IosReader() + : ConnectableReader(ReaderManagerPlugInType::NFC, QStringLiteral("NFC")) + , mDelegate() + , mCard() + , mConnected(false) + , mLastRestart(0) +{ + mReaderInfo.setBasicReader(true); + mReaderInfo.setConnected(true); + + connect(&mDelegate, &IosReaderDelegate::fireDiscoveredTag, this, &IosReader::onDiscoveredTag); + connect(&mDelegate, &IosReaderDelegate::fireDidInvalidateWithError, this, &IosReader::onDidInvalidateWithError); + + mTimerId = startTimer(500); +} + + +void IosReader::removeCard() +{ + Q_ASSERT(mCard); + mCard->invalidateTarget(); + mCard.reset(); + mReaderInfo.setCardInfo(CardInfo(CardType::NONE)); +} + + +void IosReader::stopSession(const QString& pError) +{ + qCDebug(card_nfc) << pError; + + if (pError.isEmpty()) + { + //: INFO IOS The current session was stopped without errors. + mDelegate.stopSession(tr("Scanning process has been finished successfully.")); + } + else + { + mDelegate.stopSession(pError, true); + } + + if (mCard) + { + removeCard(); + Q_EMIT fireCardRemoved(getName()); + } +} + + +IosReader::~IosReader() +{ +} + + +Card* IosReader::getCard() const +{ + if (mCard && mCard->isValid()) + { + return mCard.data(); + } + + return nullptr; +} + + +void IosReader::connectReader() +{ + mConnected = true; + mDelegate.startSession(); +} + + +void IosReader::disconnectReader(const QString& pError) +{ + stopSession(pError); + mConnected = false; +} + + +void IosReader::onDiscoveredTag(IosCard* pCard) +{ + mCard.reset(pCard); + connect(pCard, &IosCard::fireTransmitFailed, this, &IosReader::onTransmitFailed); + QSharedPointer cardConnection = createCardConnectionWorker(); + CardInfoFactory::create(cardConnection, mReaderInfo); + Q_EMIT fireCardInserted(getName()); +} + + +void IosReader::onDidInvalidateWithError(const QString& pError, bool pDoRestart) +{ + stopSession(pError); + + if (pDoRestart && mConnected) + { + const qint64 now = QDateTime::currentSecsSinceEpoch(); + if (now - mLastRestart <= 1) // Don't restart more than once per second to avoid spamming the log + { + using namespace std::chrono_literals; + QTimer::singleShot(1s, this, [this](){ + if (mConnected) + { + mDelegate.startSession(); + } + }); + } + else + { + mDelegate.startSession(); + } + mLastRestart = now; + return; + } + + mConnected = false; + Q_EMIT fireReaderDisconnected(); +} + + +void IosReader::onConnectFailed() +{ + //: ERROR IOS The connection to the card could not be established. + stopSession(tr("The connection could not be established. The process was aborted.")); + mConnected = false; + Q_EMIT fireReaderDisconnected(); +} + + +void IosReader::onTransmitFailed() +{ + //: ERROR IOS The card was removed during the communication. + stopSession(tr("The connection to the ID card has been lost. The process was aborted.")); + mConnected = false; + Q_EMIT fireReaderDisconnected(); +} diff --git a/src/card/ios/IosReaderDelegate.h b/src/card/ios/IosReaderDelegate.h new file mode 100644 index 0000000..fef2722 --- /dev/null +++ b/src/card/ios/IosReaderDelegate.h @@ -0,0 +1,42 @@ +/*! + * \brief Implementation of \ref IosReaderDelegate for iOS. + * + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "IosCard.h" + +#include + + +namespace governikus +{ + +class IosReaderDelegate + : public QObject +{ + Q_OBJECT + class Private; + + IosReaderDelegate::Private* mPrivate; + + public: + IosReaderDelegate(); + virtual ~IosReaderDelegate() override; + + void startSession(); + void stopSession(const QString& pMessage, bool pIsError = false); + + void onTagDiscovered(IosCard* pCard); + void onDidInvalidateWithError(const QString& pError, bool pDoRestart); + + Q_SIGNALS: + void fireDiscoveredTag(IosCard* pCard); + void fireDidInvalidateWithError(const QString& pError, bool pDoRestart); + + +}; + +} // namespace governikus diff --git a/src/card/ios/IosReaderDelegate.mm b/src/card/ios/IosReaderDelegate.mm new file mode 100644 index 0000000..5267e2f --- /dev/null +++ b/src/card/ios/IosReaderDelegate.mm @@ -0,0 +1,62 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "IosReaderDelegate.h" + +#include "IosReaderDelegateImpl.h" + + +using namespace governikus; + + +namespace governikus +{ + +class IosReaderDelegate::Private +{ + public: + IosReaderDelegateImpl* mIosReaderDelegateImpl; +}; + +} // namespace governikus + + +IosReaderDelegate::IosReaderDelegate() + : mPrivate(new IosReaderDelegate::Private()) +{ + IosReaderDelegateImpl* dlgt = [IosReaderDelegateImpl initWithDelegate: this]; + mPrivate->mIosReaderDelegateImpl = [dlgt retain]; +} + + +IosReaderDelegate::~IosReaderDelegate() +{ + [mPrivate->mIosReaderDelegateImpl release]; + delete mPrivate; +} + + +void IosReaderDelegate::startSession() +{ + //: INFO IOS The id card may be inserted, the authentication process may be started. + [mPrivate->mIosReaderDelegateImpl startSession: tr("Please place your device on your ID card.")]; +} + + +void IosReaderDelegate::stopSession(const QString& pMessage, bool pIsError) +{ + [mPrivate->mIosReaderDelegateImpl stopSession: pMessage isError: pIsError]; +} + + +void IosReaderDelegate::onTagDiscovered(IosCard* pCard) +{ + Q_EMIT fireDiscoveredTag(pCard); +} + + +void IosReaderDelegate::onDidInvalidateWithError(const QString& pError, bool pDoRestart) +{ + Q_EMIT fireDidInvalidateWithError(pError, pDoRestart); +} diff --git a/src/card/ios/IosReaderDelegateImpl.h b/src/card/ios/IosReaderDelegateImpl.h new file mode 100644 index 0000000..cb97aa8 --- /dev/null +++ b/src/card/ios/IosReaderDelegateImpl.h @@ -0,0 +1,27 @@ +/*! + * \brief Implementation of \ref IosReaderDelegateImpl for iOS. + * + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "IosReaderDelegate.h" + +#import + + +@interface IosReaderDelegateImpl + : NSObject + +@property governikus::IosReaderDelegate* mDelegate; +@property (retain) NFCTagReaderSession* mSession; + ++ (IosReaderDelegateImpl*) initWithDelegate: (governikus::IosReaderDelegate*) pDelegate; + +- (void) startSession: (QString)pMessage; + +- (void) stopSession: (QString)pMessage isError:(bool) pIsError; + + +@end diff --git a/src/card/ios/IosReaderDelegateImpl.mm b/src/card/ios/IosReaderDelegateImpl.mm new file mode 100644 index 0000000..5f6b01d --- /dev/null +++ b/src/card/ios/IosReaderDelegateImpl.mm @@ -0,0 +1,125 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "IosReaderDelegateImpl.h" + +#include "IosCard.h" +#include "IosCardPointer.h" + +#include + +#import + + +Q_DECLARE_LOGGING_CATEGORY(card_nfc) + + +@implementation IosReaderDelegateImpl + ++ (IosReaderDelegateImpl*) initWithDelegate:(governikus::IosReaderDelegate*)pDelegate { + IosReaderDelegateImpl* dlgt = [[IosReaderDelegateImpl alloc] init]; + dlgt.mDelegate = pDelegate; + return dlgt; + +} + + +- (id) init { + self = [super init]; + return self; +} + + +- (void) startSession: (QString)pMessage { + qCDebug(card_nfc) << "Start session"; + + if (self.mSession) + { + [self.mSession restartPolling]; + } + else + { + self.mSession = [[NFCTagReaderSession alloc] initWithPollingOption: NFCPollingISO14443 delegate: self queue: nil]; + if (self.mSession) + { + [self.mSession setAlertMessage: pMessage.toNSString()]; + [self.mSession beginSession]; + } + } +} + + +- (void) stopSession: (QString)pMessage isError:(bool) pIsError { + qCDebug(card_nfc) << "Stop session"; + + if (self.mSession) + { + if (pIsError) + { + [self.mSession invalidateSessionWithErrorMessage: pMessage.toNSString()]; + } + else + { + [self.mSession setAlertMessage: pMessage.toNSString()]; + [self.mSession invalidateSession]; + } + self.mSession = nil; + } +} + + +- (void) tagReaderSessionDidBecomeActive: (NFCTagReaderSession*)session { + qCDebug(card_nfc) << "Session activated"; + + if (session != self.mSession) + { + qCWarning(card_nfc) << "An unexpected session became active"; + return; + } +} + + +- (void) tagReaderSession: (NFCTagReaderSession*)session didInvalidateWithError:(NSError*)error { + qCDebug(card_nfc) << "Session did invalidate with error:" << error; + + if (session != self.mSession) + { + qCWarning(card_nfc) << "An unexpected session was invalidated"; + return; + } + + const bool doRestart = !(error.code == NFCReaderError::NFCReaderSessionInvalidationErrorUserCanceled || + error.code == NFCReaderError::NFCReaderErrorUnsupportedFeature); + self.mDelegate->onDidInvalidateWithError(QString::fromNSString(error.localizedDescription), doRestart); +} + + +- (void) tagReaderSession: (NFCTagReaderSession*)session didDetectTags:(NSArray<__kindof id >*)tags { + qCDebug(card_nfc) << "New tag detected"; + + if (session != self.mSession) + { + qCWarning(card_nfc) << "Detected a new tag from an unexpected session"; + return; + } + + if (tags.count > 1) + { + qCWarning(card_nfc) << "Detected more than one tag. Restarting scan"; + [session restartPolling]; + return; + } + + if (tags[0].type != NFCTagTypeISO7816Compatible) + { + qCWarning(card_nfc) << "Detected a new tag with an unexpected type:" << tags[0].type << "Restarting scan"; + [session restartPolling]; + return; + } + + self.mDelegate->onTagDiscovered(new governikus::IosCard(new governikus::IosCardPointer {tags[0]})); +} + + +@end diff --git a/src/card/ios/IosReaderManagerPlugIn.h b/src/card/ios/IosReaderManagerPlugIn.h new file mode 100644 index 0000000..c916027 --- /dev/null +++ b/src/card/ios/IosReaderManagerPlugIn.h @@ -0,0 +1,44 @@ +/*! + * \brief Implementation of \ref ReaderManagerPlugIn for NFC on iOS. + * + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "Reader.h" +#include "ReaderManagerPlugIn.h" + +#include + + +namespace governikus +{ + +class IosReaderManagerPlugIn + : public ReaderManagerPlugIn +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "governikus.ReaderManagerPlugIn" FILE "metadata.json") + Q_INTERFACES(governikus::ReaderManagerPlugIn) + + private: + QScopedPointer mIosReader; + + private Q_SLOTS: + void onReaderDisconncted(); + + public: + IosReaderManagerPlugIn(); + virtual ~IosReaderManagerPlugIn() override; + + virtual QList getReaders() const override; + + virtual void init() override; + virtual void shutdown() override; + + virtual void startScan(bool pAutoConnect) override; + virtual void stopScan(const QString& pError = QString()) override; +}; + +} // namespace governikus diff --git a/src/card/ios/IosReaderManagerPlugIn.mm b/src/card/ios/IosReaderManagerPlugIn.mm new file mode 100644 index 0000000..25aef76 --- /dev/null +++ b/src/card/ios/IosReaderManagerPlugIn.mm @@ -0,0 +1,106 @@ +/*! + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "IosReader.h" +#include "IosReaderManagerPlugIn.h" + +#include + +#import +#import + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(card_nfc) + + +void IosReaderManagerPlugIn::onReaderDisconncted() +{ + ReaderManagerPlugIn::stopScan(); +} + + +IosReaderManagerPlugIn::IosReaderManagerPlugIn() + : ReaderManagerPlugIn(ReaderManagerPlugInType::NFC, NFCTagReaderSession.readingAvailable, NFCTagReaderSession.readingAvailable) + , mIosReader(nullptr) +{ +} + + +IosReaderManagerPlugIn::~IosReaderManagerPlugIn() +{ +} + + +QList IosReaderManagerPlugIn::getReaders() const +{ + if (getInfo().isEnabled()) + { + return QList({mIosReader.data()}); + } + + return QList(); +} + + +void IosReaderManagerPlugIn::init() +{ + ReaderManagerPlugIn::init(); + + if (mIosReader) + { + return; + } + + mIosReader.reset(new IosReader()); + connect(mIosReader.data(), &IosReader::fireCardInserted, this, &IosReaderManagerPlugIn::fireCardInserted); + connect(mIosReader.data(), &IosReader::fireCardRemoved, this, &IosReaderManagerPlugIn::fireCardRemoved); + connect(mIosReader.data(), &IosReader::fireCardRetryCounterChanged, this, &IosReaderManagerPlugIn::fireCardRetryCounterChanged); + connect(mIosReader.data(), &IosReader::fireReaderPropertiesUpdated, this, &IosReaderManagerPlugIn::fireReaderPropertiesUpdated); + connect(mIosReader.data(), &IosReader::fireReaderDisconnected, this, &IosReaderManagerPlugIn::onReaderDisconncted); + qCDebug(card_nfc) << "Add reader" << mIosReader->getName(); + + if (getInfo().isEnabled()) + { + Q_EMIT fireReaderAdded(mIosReader->getName()); + } +} + + +void IosReaderManagerPlugIn::shutdown() +{ + mIosReader.reset(); +} + + +void IosReaderManagerPlugIn::startScan(bool pAutoConnect) +{ + if (isScanRunning()) + { + return; + } + + if (mIosReader) + { + mIosReader->connectReader(); + } + ReaderManagerPlugIn::startScan(pAutoConnect); +} + + +void IosReaderManagerPlugIn::stopScan(const QString& pError) +{ + if (!isScanRunning()) + { + return; + } + + if (mIosReader) + { + mIosReader->disconnectReader(pError); + } + ReaderManagerPlugIn::stopScan(); +} diff --git a/src/card/ios/metadata.json b/src/card/ios/metadata.json new file mode 100644 index 0000000..3521a09 --- /dev/null +++ b/src/card/ios/metadata.json @@ -0,0 +1,4 @@ +{ + "name" : "IosReaderManagerPlugIn", + "dependencies" : [] +} diff --git a/src/card/nfc/NfcCard.cpp b/src/card/nfc/NfcCard.cpp index e71e8cc..06ab1e0 100644 --- a/src/card/nfc/NfcCard.cpp +++ b/src/card/nfc/NfcCard.cpp @@ -16,7 +16,7 @@ Q_DECLARE_LOGGING_CATEGORY(card_nfc) void NfcCard::onError(QNearFieldTarget::Error pError, const QNearFieldTarget::RequestId& pId) { - Q_UNUSED(pId); + Q_UNUSED(pId) qCWarning(card_nfc) << "Error:" << pError; } @@ -75,7 +75,7 @@ CardReturnCode NfcCard::connect() CardReturnCode NfcCard::disconnect() { - if (!mIsValid || !mNearFieldTarget) + if (!mIsValid || mNearFieldTarget == nullptr) { qCWarning(card_nfc) << "NearFieldTarget is no longer valid"; return CardReturnCode::COMMAND_FAILED; @@ -101,7 +101,7 @@ bool NfcCard::isConnected() CardReturnCode NfcCard::transmit(const CommandApdu& pCmd, ResponseApdu& pRes) { - if (!mIsValid || !mNearFieldTarget) + if (!mIsValid || mNearFieldTarget == nullptr) { qCWarning(card_nfc) << "NearFieldTarget is no longer valid"; return CardReturnCode::COMMAND_FAILED; diff --git a/src/card/nfc/NfcCard.h b/src/card/nfc/NfcCard.h index db2ae42..f14d3cd 100644 --- a/src/card/nfc/NfcCard.h +++ b/src/card/nfc/NfcCard.h @@ -27,7 +27,7 @@ class NfcCard void onError(QNearFieldTarget::Error pError, const QNearFieldTarget::RequestId& pId); public: - NfcCard(QNearFieldTarget* pNearFieldTarget); + explicit NfcCard(QNearFieldTarget* pNearFieldTarget); virtual ~NfcCard() override; bool isValid() const; diff --git a/src/card/nfc/NfcReader.cpp b/src/card/nfc/NfcReader.cpp index 09b4a09..cf0d2b1 100644 --- a/src/card/nfc/NfcReader.cpp +++ b/src/card/nfc/NfcReader.cpp @@ -25,7 +25,7 @@ Reader::CardEvent NfcReader::updateCard() void NfcReader::targetDetected(QNearFieldTarget* pTarget) { - if (!pTarget) + if (pTarget == nullptr) { return; } @@ -55,8 +55,9 @@ void NfcReader::targetDetected(QNearFieldTarget* pTarget) void NfcReader::targetLost(QNearFieldTarget* pTarget) { qCDebug(card_nfc) << "targetLost"; - if (pTarget && mCard && mCard->invalidateTarget(pTarget)) + if (pTarget != nullptr && mCard && mCard->invalidateTarget(pTarget)) { + mCard.reset(); mReaderInfo.setCardInfo(CardInfo(CardType::NONE)); Q_EMIT fireCardRemoved(getName()); } diff --git a/src/card/nfc/NfcReaderManagerPlugIn.cpp b/src/card/nfc/NfcReaderManagerPlugIn.cpp index bf65015..4d7eadd 100644 --- a/src/card/nfc/NfcReaderManagerPlugIn.cpp +++ b/src/card/nfc/NfcReaderManagerPlugIn.cpp @@ -23,6 +23,12 @@ namespace bool isAvailable() { #ifdef Q_OS_ANDROID + #if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + QNearFieldManager manager; + return manager.isSupported(); + + #else + QAndroidJniObject context = QtAndroid::androidContext(); if (context == nullptr) { @@ -32,6 +38,8 @@ bool isAvailable() return QAndroidJniObject::callStaticObjectMethod("android/nfc/NfcAdapter", "getDefaultAdapter", "(Landroid/content/Context;)Landroid/nfc/NfcAdapter;", context.object()) != nullptr; + #endif + #else QNearFieldManager manager; return manager.isAvailable(); @@ -45,14 +53,13 @@ bool isAvailable() void NfcReaderManagerPlugIn::onNfcAdapterStateChanged(bool pEnabled) { - if (mEnabled == pEnabled) + if (getInfo().isEnabled() == pEnabled) { return; } qCDebug(card_nfc) << "NfcAdapterStateChanged:" << pEnabled; - mEnabled = pEnabled; - setReaderInfoEnabled(pEnabled); + setPlugInEnabled(pEnabled); if (pEnabled) { Q_EMIT fireReaderAdded(mNfcReader->getName()); @@ -66,7 +73,6 @@ void NfcReaderManagerPlugIn::onNfcAdapterStateChanged(bool pEnabled) NfcReaderManagerPlugIn::NfcReaderManagerPlugIn() : ReaderManagerPlugIn(ReaderManagerPlugInType::NFC, isAvailable()) - , mEnabled(false) , mNfcReader(nullptr) { } @@ -79,7 +85,7 @@ NfcReaderManagerPlugIn::~NfcReaderManagerPlugIn() QList NfcReaderManagerPlugIn::getReaders() const { - if (mEnabled) + if (getInfo().isEnabled()) { return QList({mNfcReader.data()}); } @@ -105,7 +111,7 @@ void NfcReaderManagerPlugIn::init() connect(mNfcReader.data(), &NfcReader::fireNfcAdapterStateChanged, this, &NfcReaderManagerPlugIn::onNfcAdapterStateChanged); qCDebug(card_nfc) << "Add reader" << mNfcReader->getName(); - if (mEnabled) + if (getInfo().isEnabled()) { Q_EMIT fireReaderAdded(mNfcReader->getName()); } diff --git a/src/card/nfc/NfcReaderManagerPlugIn.h b/src/card/nfc/NfcReaderManagerPlugIn.h index 16ac62b..df2c51c 100644 --- a/src/card/nfc/NfcReaderManagerPlugIn.h +++ b/src/card/nfc/NfcReaderManagerPlugIn.h @@ -24,7 +24,6 @@ class NfcReaderManagerPlugIn Q_INTERFACES(governikus::ReaderManagerPlugIn) private: - bool mEnabled; QScopedPointer mNfcReader; private Q_SLOTS: diff --git a/src/card/pcsc/PcscCard.cpp b/src/card/pcsc/PcscCard.cpp index add8b08..cd0ab01 100644 --- a/src/card/pcsc/PcscCard.cpp +++ b/src/card/pcsc/PcscCard.cpp @@ -233,13 +233,8 @@ PCSC_RETURNCODE PcscCard::transmit(const QByteArray& pSendBuffer, QByteArray& pR return PcscUtils::Scard_E_Proto_Mismatch; } - SCARD_IO_REQUEST recvPci; - recvPci.dwProtocol = mProtocol; - recvPci.cbPciLength = sizeof(SCARD_IO_REQUEST); - - pReceiveBuffer.fill(0x00, 8192); - PCSC_INT bytesReceived = static_cast(pReceiveBuffer.size()); - PCSC_RETURNCODE returnCode = transmit(pSendBuffer, pReceiveBuffer, sendPci, recvPci, bytesReceived); + auto [returnCode, buffer] = transmit(pSendBuffer, sendPci); + pReceiveBuffer = buffer; /* * Reconnecting makes only sense, when no secure messaging channel is active. @@ -258,51 +253,62 @@ PCSC_RETURNCODE PcscCard::transmit(const QByteArray& pSendBuffer, QByteArray& pR returnCode = SCardBeginTransaction(mCardHandle); qCDebug(card_pcsc) << "SCardBeginTransaction:" << PcscUtils::toString(returnCode); - bytesReceived = static_cast(pReceiveBuffer.size()); - returnCode = transmit(pSendBuffer, pReceiveBuffer, sendPci, recvPci, bytesReceived); + auto [retryReturnCode, retryBuffer] = transmit(pSendBuffer, sendPci); + returnCode = retryReturnCode; + pReceiveBuffer = retryBuffer; } - if (returnCode != PcscUtils::Scard_S_Success) - { - return returnCode; - } - if (bytesReceived > INT_MAX) - { - qCCritical(card_pcsc) << "Max allowed receive buffer size exceeded"; - Q_ASSERT(bytesReceived <= INT_MAX); - return PcscUtils::Scard_F_Unknown_Error; - } - pReceiveBuffer.resize(static_cast(bytesReceived)); - qCDebug(card_pcsc) << "SCardTransmit resBuffer:" << pReceiveBuffer.toHex(); - if (pReceiveBuffer.size() < 2) - { - qCCritical(card_pcsc) << "Response buffer smaller than 2"; - return PcscUtils::Scard_F_Unknown_Error; - } - return PcscUtils::Scard_S_Success; -} - - -PCSC_RETURNCODE PcscCard::transmit(const QByteArray& pSendBuffer, - QByteArray& pReceiveBuffer, - const SCARD_IO_REQUEST* pSendPci, - SCARD_IO_REQUEST& pRecvPci, - PCSC_INT& pBytesReceived) -{ - qCDebug(card_pcsc) << "SCardTransmit cmdBuffer:" << pSendBuffer.toHex(); - PCSC_RETURNCODE returnCode = SCardTransmit(mCardHandle, pSendPci, reinterpret_cast(pSendBuffer.data()), static_cast(pSendBuffer.size()), &pRecvPci, reinterpret_cast(pReceiveBuffer.data()), &pBytesReceived); - qCDebug(card_pcsc) << "SCardTransmit for" << mReader->getName() << ':' << PcscUtils::toString(returnCode); - return returnCode; } -CardReturnCode PcscCard::establishPaceChannel(PacePasswordId pPasswordId, +PcscCard::CardResult PcscCard::transmit(const QByteArray& pSendBuffer, + const SCARD_IO_REQUEST* pSendPci) +{ + SCARD_IO_REQUEST recvPci; + recvPci.dwProtocol = mProtocol; + recvPci.cbPciLength = sizeof(SCARD_IO_REQUEST); + + QByteArray data(8192, '\0'); + auto dataReceived = static_cast(data.size()); + + qCDebug(card_pcsc) << "SCardTransmit cmdBuffer:" << pSendBuffer.toHex(); + const PCSC_RETURNCODE returnCode = SCardTransmit(mCardHandle, + pSendPci, + reinterpret_cast(pSendBuffer.data()), + static_cast(pSendBuffer.size()), + &recvPci, + reinterpret_cast(data.data()), + &dataReceived); + + qCDebug(card_pcsc) << "SCardTransmit for" << mReader->getName() << ':' << PcscUtils::toString(returnCode); + + if (dataReceived > INT_MAX) + { + qCCritical(card_pcsc) << "Max allowed receive buffer size exceeded"; + Q_ASSERT(dataReceived <= INT_MAX); + return {PcscUtils::Scard_F_Unknown_Error}; + } + + data.resize(static_cast(dataReceived)); + qCDebug(card_pcsc) << "SCardTransmit resBuffer:" << data.toHex(); + + if (data.size() < 2) + { + qCCritical(card_pcsc) << "Response buffer smaller than 2"; + return {PcscUtils::Scard_F_Unknown_Error}; + } + + return {returnCode, data}; +} + + +EstablishPaceChannelOutput PcscCard::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, - EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) + quint8 pTimeoutSeconds) { - Q_UNUSED(pTimeoutSeconds); + Q_UNUSED(pTimeoutSeconds) if (!mReader->hasFeature(FeatureID::EXECUTE_PACE)) { return CardReturnCode::COMMAND_FAILED; @@ -314,16 +320,16 @@ CardReturnCode PcscCard::establishPaceChannel(PacePasswordId pPasswordId, builder.setChat(pChat); builder.setCertificateDescription(pCertificateDescription); - QByteArray controlRes; - PCSC_RETURNCODE returnCode = control(cmdID, builder.createCommandData(), controlRes); + auto [returnCode, controlRes] = control(cmdID, builder.createCommandData()); if (returnCode != PcscUtils::Scard_S_Success) { qCWarning(card_pcsc) << "Control to establish PACE channel failed"; return CardReturnCode::COMMAND_FAILED; } - pChannelOutput.parse(controlRes, pPasswordId); - return pChannelOutput.getPaceReturnCode(); + EstablishPaceChannelOutput output; + output.parse(controlRes, pPasswordId); + return output; } @@ -336,8 +342,7 @@ CardReturnCode PcscCard::destroyPaceChannel() PCSC_INT cmdID = mReader->getFeatureValue(FeatureID::EXECUTE_PACE); DestroyPaceChannelBuilder builder; - QByteArray controlRes; - PCSC_RETURNCODE returnCode = control(cmdID, builder.createCommandData(), controlRes); + auto [returnCode, controlRes] = control(cmdID, builder.createCommandData()); if (returnCode != PcscUtils::Scard_S_Success) { qCWarning(card_pcsc) << "Control to destroy PACE channel failed"; @@ -347,17 +352,17 @@ CardReturnCode PcscCard::destroyPaceChannel() } -PCSC_RETURNCODE PcscCard::control(PCSC_INT pCntrCode, const QByteArray& pCntrInput, QByteArray& pCntrOutput) +PcscCard::CardResult PcscCard::control(PCSC_INT pCntrCode, const QByteArray& pCntrInput) { - char buffer[2048]; + QByteArray buffer(2048, '\0'); PCSC_INT len = 0; qCDebug(card_pcsc) << "SCardControl cmdBuffer:" << pCntrInput.toHex(); PCSC_RETURNCODE returnCode = SCardControl(mCardHandle, pCntrCode, pCntrInput.constData(), static_cast(pCntrInput.size()), - buffer, - sizeof(buffer), + buffer.data(), + static_cast(buffer.size()), &len); if (returnCode != PcscUtils::Scard_S_Success) @@ -365,22 +370,16 @@ PCSC_RETURNCODE PcscCard::control(PCSC_INT pCntrCode, const QByteArray& pCntrInp len = 0; } - if (sizeof(buffer) < len) + if (buffer.size() < static_cast(len)) { qCCritical(card_pcsc) << "Buffer size smaller than read length"; - Q_ASSERT(sizeof(buffer) >= len); - return PcscUtils::Scard_F_Unknown_Error; + Q_ASSERT(buffer.size() >= static_cast(len)); + return {PcscUtils::Scard_F_Unknown_Error}; } - if (len > INT_MAX) - { - qCCritical(card_pcsc) << "Read length bigger than INT_MAX"; - Q_ASSERT(len <= INT_MAX); - return PcscUtils::Scard_F_Unknown_Error; - } - pCntrOutput.append(buffer, static_cast(len)); - qCDebug(card_pcsc) << "SCardControl for" << mReader->getName() << ':' << PcscUtils::toString(returnCode) << pCntrOutput.toHex(); - return returnCode; + buffer.resize(static_cast(len)); + qCDebug(card_pcsc) << "SCardControl for" << mReader->getName() << ':' << PcscUtils::toString(returnCode) << buffer.toHex(); + return {returnCode, buffer}; } @@ -393,9 +392,8 @@ CardReturnCode PcscCard::setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pRespon PCSC_INT cmdID = mReader->getFeatureValue(FeatureID::MODIFY_PIN_DIRECT); PinModify pinModify(pTimeoutSeconds); - QByteArray controlRes; - PCSC_RETURNCODE pcscReturnCode = control(cmdID, pinModify.createCcidForPcsc(), controlRes); - if (pcscReturnCode != PcscUtils::Scard_S_Success) + auto [returnCode, controlRes] = control(cmdID, pinModify.createCcidForPcsc()); + if (returnCode != PcscUtils::Scard_S_Success) { qCWarning(card_pcsc) << "Modify PIN failed"; return CardReturnCode::COMMAND_FAILED; diff --git a/src/card/pcsc/PcscCard.h b/src/card/pcsc/PcscCard.h index 7b4e838..c6ed3fe 100644 --- a/src/card/pcsc/PcscCard.h +++ b/src/card/pcsc/PcscCard.h @@ -25,6 +25,12 @@ class PcscCard Q_OBJECT private: + struct CardResult + { + PCSC_RETURNCODE mReturnCode; + QByteArray mResponse = QByteArray(); + }; + QPointer mReader; PCSC_INT mProtocol; SCARDCONTEXT mContextHandle; @@ -33,19 +39,15 @@ class PcscCard PCSC_RETURNCODE transmit(const QByteArray& pSendBuffer, QByteArray& pReceiveBuffer); - PCSC_RETURNCODE transmit(const QByteArray& pSendBuffer, - QByteArray& pReceiveBuffer, - const SCARD_IO_REQUEST* pSendPci, - SCARD_IO_REQUEST& pRecvPci, - PCSC_INT& pBytesReceived); + CardResult transmit(const QByteArray& pSendBuffer, const SCARD_IO_REQUEST* pSendPci); - PCSC_RETURNCODE control(PCSC_INT pCntrCode, const QByteArray& pCntrInput, QByteArray& pCntrOutput); + CardResult control(PCSC_INT pCntrCode, const QByteArray& pCntrInput); private Q_SLOTS: void sendSCardStatus(); public: - PcscCard(PcscReader* pPcscReader); + explicit PcscCard(PcscReader* pPcscReader); virtual ~PcscCard() override; virtual CardReturnCode connect() override; @@ -54,7 +56,7 @@ class PcscCard virtual CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; - virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) override; + virtual EstablishPaceChannelOutput establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, quint8 pTimeoutSeconds) override; virtual CardReturnCode destroyPaceChannel() override; diff --git a/src/card/pcsc/PcscReader.cpp b/src/card/pcsc/PcscReader.cpp index b97ae1b..da2aeff 100644 --- a/src/card/pcsc/PcscReader.cpp +++ b/src/card/pcsc/PcscReader.cpp @@ -34,7 +34,7 @@ PcscReader::PcscReader(const QString& pReaderName) memset(&mReaderState, 0, sizeof(SCARD_READERSTATE)); #if defined(Q_OS_WIN) && defined(UNICODE) - wchar_t* name = new wchar_t[pReaderName.size() + 1](); + wchar_t* name = new wchar_t[static_cast(pReaderName.size()) + 1](); pReaderName.toWCharArray(name); mReaderState.szReader = name; #else @@ -211,14 +211,12 @@ Reader::CardEvent PcscReader::updateCard() CardInfoFactory::create(cardConnection, mReaderInfo); qCDebug(card_pcsc) << "Card detected:" << mReaderInfo.getCardInfo(); - if (mReaderInfo.hasCard() && !mReaderInfo.hasEidCard()) - { - qCDebug(card_pcsc) << "Unknown card detected, retrying."; - } - else + if (mReaderInfo.hasEidCard() || mReaderInfo.hasPassport()) { break; } + + qCDebug(card_pcsc) << "Unknown card detected, retrying."; } return CardEvent::CARD_INSERTED; diff --git a/src/card/pcsc/PcscReaderFeature.cpp b/src/card/pcsc/PcscReaderFeature.cpp index 05731b9..7002594 100644 --- a/src/card/pcsc/PcscReaderFeature.cpp +++ b/src/card/pcsc/PcscReaderFeature.cpp @@ -14,7 +14,7 @@ using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(card_pcsc) -PcscReaderFeature::PcscReaderFeature(const char* pFeaturesTLV, PCSC_INT pLength) +PcscReaderFeature::PcscReaderFeature(const char* const pFeaturesTLV, PCSC_INT pLength) : mFeatures() { if (pFeaturesTLV == nullptr) @@ -23,8 +23,8 @@ PcscReaderFeature::PcscReaderFeature(const char* pFeaturesTLV, PCSC_INT pLength) return; } - const uchar* runner = reinterpret_cast(pFeaturesTLV); - const uchar* end = reinterpret_cast(pFeaturesTLV + pLength); + const auto* runner = reinterpret_cast(pFeaturesTLV); + const auto* const end = reinterpret_cast(pFeaturesTLV + pLength); for (; runner + 6 <= end;) { if (!Enum::isValue(*runner)) @@ -33,7 +33,7 @@ PcscReaderFeature::PcscReaderFeature(const char* pFeaturesTLV, PCSC_INT pLength) continue; } - FeatureID fid = static_cast(*runner); + auto fid = static_cast(*runner); ++runner; // skip length byte (always 1 byte : 0x04) diff --git a/src/card/pcsc/PcscReaderFeature.h b/src/card/pcsc/PcscReaderFeature.h index a9d5ec4..be4b0cc 100644 --- a/src/card/pcsc/PcscReaderFeature.h +++ b/src/card/pcsc/PcscReaderFeature.h @@ -41,7 +41,7 @@ class PcscReaderFeature friend QDebug operator<<(QDebug, const PcscReaderFeature&); public: - PcscReaderFeature(const char* pFeaturesTLV, PCSC_INT pLength = 0); + PcscReaderFeature(const char* const pFeaturesTLV, PCSC_INT pLength = 0); bool contains(FeatureID pFeatureID) const; diff --git a/src/card/pcsc/PcscReaderManagerPlugIn.cpp b/src/card/pcsc/PcscReaderManagerPlugIn.cpp index 5e15714..e31d30d 100644 --- a/src/card/pcsc/PcscReaderManagerPlugIn.cpp +++ b/src/card/pcsc/PcscReaderManagerPlugIn.cpp @@ -5,6 +5,7 @@ #include "PcscReaderManagerPlugIn.h" #include "PcscReader.h" + #include @@ -24,7 +25,7 @@ PcscReaderManagerPlugIn::PcscReaderManagerPlugIn() setObjectName(QStringLiteral("PcscReaderManager")); #ifdef PCSCLITE_VERSION_NUMBER - setReaderInfoValue(ReaderManagerPlugInInfo::Key::PCSC_LITE_VERSION, QStringLiteral(PCSCLITE_VERSION_NUMBER)); + setPlugInValue(ReaderManagerPlugInInfo::Key::PCSC_LITE_VERSION, QStringLiteral(PCSCLITE_VERSION_NUMBER)); #endif initReaderState(); @@ -51,17 +52,17 @@ QList PcscReaderManagerPlugIn::getReaders() const void PcscReaderManagerPlugIn::startScan(bool pAutoConnect) { - Q_UNUSED(pAutoConnect); + Q_UNUSED(pAutoConnect) PCSC_RETURNCODE returnCode = SCardEstablishContext(SCARD_SCOPE_USER, nullptr, nullptr, &mContextHandle); - setReaderInfoEnabled(returnCode == PcscUtils::Scard_S_Success); + setPlugInEnabled(returnCode == PcscUtils::Scard_S_Success); qCDebug(card_pcsc) << "SCardEstablishContext:" << PcscUtils::toString(returnCode); if (returnCode != PcscUtils::Scard_S_Success) { qCWarning(card_pcsc) << "Not started: Cannot establish context"; } - if (!mTimerId) + if (mTimerId == 0) { mTimerId = startTimer(500); } @@ -69,7 +70,7 @@ void PcscReaderManagerPlugIn::startScan(bool pAutoConnect) } -void PcscReaderManagerPlugIn::stopScan() +void PcscReaderManagerPlugIn::stopScan(const QString& pError) { if (mTimerId) { @@ -86,8 +87,8 @@ void PcscReaderManagerPlugIn::stopScan() qCWarning(card_pcsc) << "Error releasing context"; } } - updateReaders(); - ReaderManagerPlugIn::stopScan(); + removeReaders(mReaders.keys()); + ReaderManagerPlugIn::stopScan(pError); } @@ -113,13 +114,11 @@ void PcscReaderManagerPlugIn::initReaderState() void PcscReaderManagerPlugIn::updateReaders() { - QStringList readersToRemove(mReaders.keys()); QStringList readersToAdd; - PCSC_RETURNCODE returnCode = readReaderNames(readersToAdd); if (returnCode != PcscUtils::Scard_S_Success && returnCode != PcscUtils::Scard_E_No_Readers_Available) { - qCWarning(card_pcsc) << "Cannot update readers"; + qCWarning(card_pcsc) << "Cannot update readers, returnCode:" << returnCode; if (returnCode == PcscUtils::Scard_E_No_Service && mTimerId != 0) { @@ -151,6 +150,7 @@ void PcscReaderManagerPlugIn::updateReaders() } } + QStringList readersToRemove(mReaders.keys()); for (QMutableListIterator it(readersToAdd); it.hasNext();) { QString readerName = it.next(); @@ -161,10 +161,7 @@ void PcscReaderManagerPlugIn::updateReaders() } } - for (const auto& readerName : qAsConst(readersToRemove)) - { - removeReader(readerName); - } + removeReaders(readersToRemove); for (QMutableListIterator iterator(readersToAdd); iterator.hasNext();) { @@ -176,7 +173,7 @@ void PcscReaderManagerPlugIn::updateReaders() connect(reader, &Reader::fireCardRemoved, this, &PcscReaderManagerPlugIn::fireCardRemoved); connect(reader, &Reader::fireCardRetryCounterChanged, this, &PcscReaderManagerPlugIn::fireCardRetryCounterChanged); - qCDebug(card_pcsc) << "fireReaderAdded:" << readerName; + qCDebug(card_pcsc) << "fireReaderAdded:" << readerName << "(" << mReaders.size() << "reader in total )"; Q_EMIT fireReaderAdded(readerName); } } @@ -196,7 +193,12 @@ QString PcscReaderManagerPlugIn::extractReaderName(PCSC_CHAR_PTR pReaderPointer) void PcscReaderManagerPlugIn::removeReader(const QString& pReaderName) { - Q_ASSERT(mReaders.contains(pReaderName)); + if (!mReaders.contains(pReaderName)) + { + qCDebug(card_pcsc) << "Reader not present:" << pReaderName; + Q_ASSERT(false); + } + delete mReaders.take(pReaderName); qCDebug(card_pcsc) << "fireReaderRemoved:" << pReaderName; @@ -204,15 +206,24 @@ void PcscReaderManagerPlugIn::removeReader(const QString& pReaderName) } +void PcscReaderManagerPlugIn::removeReaders(const QStringList& pReaderNames) +{ + for (const auto& readerName : pReaderNames) + { + removeReader(readerName); + } +} + + PCSC_RETURNCODE PcscReaderManagerPlugIn::readReaderNames(QStringList& pReaderNames) { - if (!mContextHandle) + if (mContextHandle == 0) { return PcscUtils::Scard_E_Invalid_Handle; } QVarLengthArray readers; - PCSC_INT maxReadersSize = static_cast(readers.capacity()); + auto maxReadersSize = static_cast(readers.capacity()); PCSC_RETURNCODE returnCode = SCardListReaders(mContextHandle, nullptr, readers.data(), &maxReadersSize); if (returnCode != PcscUtils::Scard_S_Success) { diff --git a/src/card/pcsc/PcscReaderManagerPlugIn.h b/src/card/pcsc/PcscReaderManagerPlugIn.h index a220041..4b683f7 100644 --- a/src/card/pcsc/PcscReaderManagerPlugIn.h +++ b/src/card/pcsc/PcscReaderManagerPlugIn.h @@ -11,6 +11,7 @@ #include "ReaderManagerPlugIn.h" #include +#include namespace governikus @@ -35,6 +36,7 @@ class PcscReaderManagerPlugIn void initReaderState(); inline QString extractReaderName(PCSC_CHAR_PTR pReaderPointer); void removeReader(const QString& pReaderName); + void removeReaders(const QStringList& pReaderNames); protected: void timerEvent(QTimerEvent* pEvent) override; @@ -46,7 +48,7 @@ class PcscReaderManagerPlugIn QList getReaders() const override; virtual void startScan(bool pAutoConnect) override; - virtual void stopScan() override; + virtual void stopScan(const QString& pError = QString()) override; }; } // namespace governikus diff --git a/src/card/pcsc/PcscReaderPaceCapability.cpp b/src/card/pcsc/PcscReaderPaceCapability.cpp index b6a0c7f..589c69d 100644 --- a/src/card/pcsc/PcscReaderPaceCapability.cpp +++ b/src/card/pcsc/PcscReaderPaceCapability.cpp @@ -14,7 +14,7 @@ using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(card_pcsc) -PcscReaderPaceCapability::PcscReaderPaceCapability(const char* pCapabilitiesTLV, PCSC_INT pLength) +PcscReaderPaceCapability::PcscReaderPaceCapability(const char* const pCapabilitiesTLV, PCSC_INT pLength) : mPaceCapabilities() { if (pCapabilitiesTLV == nullptr || pLength != 7) @@ -29,7 +29,8 @@ PcscReaderPaceCapability::PcscReaderPaceCapability(const char* pCapabilitiesTLV, } // in contrast to PCSC 10 Amendment 1: the output data of GetReaderPACECapabilities on Reiner SCT Konfort is of size 1! - for (PaceCapabilityId capability : Enum::getList()) + const auto list = Enum::getList(); + for (PaceCapabilityId capability : list) { if (pCapabilitiesTLV[6] & static_cast(capability)) { diff --git a/src/card/pcsc/PcscReaderPaceCapability.h b/src/card/pcsc/PcscReaderPaceCapability.h index 1bc1b8e..a1df537 100644 --- a/src/card/pcsc/PcscReaderPaceCapability.h +++ b/src/card/pcsc/PcscReaderPaceCapability.h @@ -25,7 +25,7 @@ class PcscReaderPaceCapability friend QDebug operator<<(QDebug, const PcscReaderPaceCapability&); public: - PcscReaderPaceCapability(const char* pCapabilitiesTLV, PCSC_INT pLength = 0); + PcscReaderPaceCapability(const char* const pCapabilitiesTLV, PCSC_INT pLength = 0); bool contains(PaceCapabilityId pPaceCapabilityId) const; }; diff --git a/src/card/pcsc/PcscUtils.cpp b/src/card/pcsc/PcscUtils.cpp index 18093ce..55e7816 100644 --- a/src/card/pcsc/PcscUtils.cpp +++ b/src/card/pcsc/PcscUtils.cpp @@ -10,7 +10,7 @@ using namespace governikus; QString PcscUtils::toString(PCSC_RETURNCODE pCode) { const auto& metaEnum = QMetaEnum::fromType(); - const char* name = metaEnum.valueToKey(static_cast(pCode)); + const char* const name = metaEnum.valueToKey(static_cast(pCode)); if (Q_UNLIKELY(name == nullptr)) { return QStringLiteral("UNKNOWN_STATE (%1)").arg(pCode, 8, 16, QLatin1Char('0')); diff --git a/src/card/pcsc/PcscUtils.h b/src/card/pcsc/PcscUtils.h index 8fc82c4..2e4c4a0 100644 --- a/src/card/pcsc/PcscUtils.h +++ b/src/card/pcsc/PcscUtils.h @@ -23,26 +23,26 @@ * an abstraction layer for those data types. */ #ifdef Q_OS_WIN -typedef LONG PCSC_RETURNCODE; -typedef DWORD PCSC_INT; -typedef TCHAR PCSC_CHAR; -typedef TCHAR* PCSC_CHAR_PTR; -typedef LPBYTE PCSC_UCHAR_PTR; -typedef LPCBYTE PCSC_CUCHAR_PTR; +using PCSC_RETURNCODE = LONG; +using PCSC_INT = DWORD; +using PCSC_CHAR = TCHAR; +using PCSC_CHAR_PTR = TCHAR *; +using PCSC_UCHAR_PTR = LPBYTE; +using PCSC_CUCHAR_PTR = LPCBYTE; #elif defined Q_OS_MACOS -typedef int32_t PCSC_RETURNCODE; -typedef uint32_t PCSC_INT; -typedef char PCSC_CHAR; -typedef char* PCSC_CHAR_PTR; -typedef uchar* PCSC_UCHAR_PTR; -typedef const uchar* PCSC_CUCHAR_PTR; +using PCSC_RETURNCODE = int32_t; +using PCSC_INT = uint32_t; +using PCSC_CHAR = char; +using PCSC_CHAR_PTR = char*; +using PCSC_UCHAR_PTR = uchar *; +using PCSC_CUCHAR_PTR = const uchar *; #elif defined Q_OS_UNIX -typedef LONG PCSC_RETURNCODE; -typedef DWORD PCSC_INT; -typedef char PCSC_CHAR; -typedef char* PCSC_CHAR_PTR; -typedef uchar* PCSC_UCHAR_PTR; -typedef const uchar* PCSC_CUCHAR_PTR; +using PCSC_RETURNCODE = LONG; +using PCSC_INT = DWORD; +using PCSC_CHAR = char; +using PCSC_CHAR_PTR = char*; +using PCSC_UCHAR_PTR = uchar *; +using PCSC_CUCHAR_PTR = const uchar *; #endif @@ -128,7 +128,7 @@ class PcscUtils Scard_W_Cancelled_By_User = static_cast(SCARD_W_CANCELLED_BY_USER), /**< The user pressed "Cancel" on a Smart Card Selection Dialog. */ Scard_W_Card_Not_Authenticated = static_cast(SCARD_W_CARD_NOT_AUTHENTICATED) /**< No PIN was presented to the smart card. */ }; - Q_ENUM(PcscReturnCode); + Q_ENUM(PcscReturnCode) static QString toString(PCSC_RETURNCODE pCode); }; diff --git a/src/config.h.in b/src/config.h.in index c856c91..b420c0e 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -12,20 +12,34 @@ #define VERSION_PATCH @PROJECT_VERSION_PATCH@ #define VERSION_TWEAK @PROJECT_VERSION_TWEAK@ -#ifdef __BASE_FILE__ - constexpr inline bool isComparableSep(char pFirst, char pSecond) - { - return (pFirst == '\\' && pSecond == '/') || (pFirst == '/' && pSecond == '\\'); - } +#ifndef RC_INVOKED +#if defined(__BASE_FILE__) && __has_include() +#include - constexpr inline bool isEqual(const char* pFirst, const char* pSecond) - { - return (*pFirst && *pSecond) ? ((*pFirst == *pSecond || isComparableSep(*pFirst, *pSecond)) && isEqual(pFirst + 1, pSecond + 1)) : (!*pFirst && !*pSecond); - } +constexpr bool isPathSeparator(char pChar) +{ + return pChar == '/' || pChar == '\\'; +} - static_assert(isEqual("@CMAKE_CURRENT_SOURCE_DIR@/main.cpp", __BASE_FILE__), "config.h include is allowed in main.cpp only! Provided: " __BASE_FILE__); + +constexpr const char* stripPath(const char* pPath) +{ + auto result = pPath; + for (auto iter = pPath; *iter; ++iter) + { + if (isPathSeparator(*iter) && *(iter + 1)) + { + result = iter + 1; + } + } + return result; +} + + +static_assert(std::string_view(stripPath(__BASE_FILE__)) == "main.cpp", "config.h include is allowed in main.cpp only!"); #endif +#endif // RC_INVOKED -/* DO NOT INCLUDE THIS - Use QCoreApplication or VersionNumber! -*/ + +// DO NOT INCLUDE THIS +// Use QCoreApplication or VersionNumber! diff --git a/src/configuration/CallCost.cpp b/src/configuration/CallCost.cpp index 2a6a6bf..0ba2188 100644 --- a/src/configuration/CallCost.cpp +++ b/src/configuration/CallCost.cpp @@ -12,7 +12,7 @@ using namespace governikus; namespace { -inline double getValue(const QJsonValue& pJson, const char* pSection, const char* pSubSection) +inline double getValue(const QJsonValue& pJson, const char* const pSection, const char* const pSubSection) { return pJson.toObject()[QLatin1String(pSection)].toObject()[QLatin1String(pSubSection)].toDouble(); } diff --git a/src/configuration/CallCost.h b/src/configuration/CallCost.h index 94392f1..e37f8c0 100644 --- a/src/configuration/CallCost.h +++ b/src/configuration/CallCost.h @@ -22,8 +22,8 @@ class CallCost double mMobileCentsPerMinute, mMobileCentsPerCall; public: - CallCost(int pFreeSeconds = 0, double pLandlineCentsPerMinute = 0.0, double pLandlineCentsPerCall = 0.0, double pMobileCentsPerMinute = 0.0, double pMobileCentsPerCall = 0.0); - CallCost(const QJsonValue& pJson); + explicit CallCost(int pFreeSeconds = 0, double pLandlineCentsPerMinute = 0.0, double pLandlineCentsPerCall = 0.0, double pMobileCentsPerMinute = 0.0, double pMobileCentsPerCall = 0.0); + explicit CallCost(const QJsonValue& pJson); void load(); void save(); diff --git a/src/configuration/ProviderConfiguration.h b/src/configuration/ProviderConfiguration.h index 9c41abf..0e7e5aa 100644 --- a/src/configuration/ProviderConfiguration.h +++ b/src/configuration/ProviderConfiguration.h @@ -16,6 +16,7 @@ #include #include +class test_HistoryModel; namespace governikus { @@ -25,6 +26,7 @@ class ProviderConfiguration { Q_OBJECT friend class Env; + friend class ::test_HistoryModel; private: const QSharedPointer mUpdatableFile; diff --git a/src/configuration/ProviderConfigurationInfo.cpp b/src/configuration/ProviderConfigurationInfo.cpp index a4d727d..e2f7b8f 100644 --- a/src/configuration/ProviderConfigurationInfo.cpp +++ b/src/configuration/ProviderConfigurationInfo.cpp @@ -63,6 +63,33 @@ bool ProviderConfigurationInfo::operator ==(const ProviderConfigurationInfo& pOt } +bool ProviderConfigurationInfo::matchWithSubjectUrl(const QString& pSubjectUrl) const +{ + if (pSubjectUrl.isEmpty()) + { + return false; + } + const QString subjectUrlHost = QUrl(pSubjectUrl).host(); + + // Check provider address host. + if (!getAddress().isEmpty() && QUrl(getAddress()).host() == subjectUrlHost) + { + return true; + } + + // Check subject urls. + for (const auto& subjectUrl : getSubjectUrls()) + { + if (!subjectUrl.isEmpty() && QUrl(subjectUrl).host() == subjectUrlHost) + { + return true; + } + } + + return false; +} + + const LanguageString& ProviderConfigurationInfo::getShortName() const { return d->mShortName; diff --git a/src/configuration/ProviderConfigurationInfo.h b/src/configuration/ProviderConfigurationInfo.h index 59be01b..f8f5ab8 100644 --- a/src/configuration/ProviderConfigurationInfo.h +++ b/src/configuration/ProviderConfigurationInfo.h @@ -122,6 +122,7 @@ class ProviderConfigurationInfo void setTcTokenUrl(const QString& pTcTokenUrl); bool operator ==(const ProviderConfigurationInfo& pOther) const; + bool matchWithSubjectUrl(const QString& pSubjectUrl) const; const LanguageString& getShortName() const; const LanguageString& getLongName() const; diff --git a/src/configuration/ReaderConfiguration.cpp b/src/configuration/ReaderConfiguration.cpp index 28120f8..b8f6684 100644 --- a/src/configuration/ReaderConfiguration.cpp +++ b/src/configuration/ReaderConfiguration.cpp @@ -4,7 +4,6 @@ #include "ReaderConfiguration.h" -#include "FileDestination.h" #include "FileProvider.h" #include "FuncUtils.h" #include "ReaderConfigurationParser.h" diff --git a/src/configuration/ReaderConfigurationInfo.cpp b/src/configuration/ReaderConfigurationInfo.cpp index a479812..2eaf6c8 100644 --- a/src/configuration/ReaderConfigurationInfo.cpp +++ b/src/configuration/ReaderConfigurationInfo.cpp @@ -10,6 +10,7 @@ using namespace governikus; ReaderConfigurationInfo::ReaderConfigurationInfo() +//: LABEL ALL_PLATFORMS : ReaderConfigurationInfo(tr("Unknown reader")) { } diff --git a/src/core/DiagnosisAntivirusDetection.h b/src/core/DiagnosisAntivirusDetection.h index 4842856..c702813 100644 --- a/src/core/DiagnosisAntivirusDetection.h +++ b/src/core/DiagnosisAntivirusDetection.h @@ -11,6 +11,8 @@ #include class test_DiagnosisAntivirusDetection; +class test_DiagnosisTreeModel; +class test_DiagnosisModel; namespace governikus { @@ -52,6 +54,8 @@ class DiagnosisAntivirusDetection private: friend class ::test_DiagnosisAntivirusDetection; + friend class ::test_DiagnosisTreeModel; + friend class ::test_DiagnosisModel; #if defined(Q_OS_WIN) QSharedPointer mProcess; diff --git a/src/core/DiagnosisConnectionTest.cpp b/src/core/DiagnosisConnectionTest.cpp index 9e6faa7..825af91 100644 --- a/src/core/DiagnosisConnectionTest.cpp +++ b/src/core/DiagnosisConnectionTest.cpp @@ -113,7 +113,9 @@ QString DiagnosisConnectionTest::getProxyTypeAsQString(QNetworkProxy::ProxyType case QNetworkProxy::FtpCachingProxy: return QStringLiteral("FtpCachingProxy"); } + Q_UNREACHABLE(); + return QString(); } @@ -174,7 +176,7 @@ void DiagnosisConnectionTest::startConnectionTest() mProxyCapabilities = getProxyCapabilitiesAsQString(proxy.capabilities()); } - const QUrl& testUrl = QUrl(SecureStorage::getInstance().getUpdateServerBaseUrl()); + const QUrl& testUrl = QUrl(Env::getSingleton()->getUpdateServerBaseUrl()); if (mIsProxySet) { mPingSocketToProxy.reset(); diff --git a/src/core/DiagnosisConnectionTest.h b/src/core/DiagnosisConnectionTest.h index 8bab98b..a2c89a0 100644 --- a/src/core/DiagnosisConnectionTest.h +++ b/src/core/DiagnosisConnectionTest.h @@ -13,6 +13,8 @@ #include class test_DiagnosisConnectionTest; +class test_DiagnosisModel; +class test_DiagnosisTreeModel; namespace governikus { @@ -24,6 +26,8 @@ class DiagnosisConnectionTest private: friend class ::test_DiagnosisConnectionTest; + friend class ::test_DiagnosisModel; + friend class ::test_DiagnosisTreeModel; bool mIsProxySet; QString mProxyHostName; QString mProxyPort; diff --git a/src/core/DiagnosisFirewallDetection.cpp b/src/core/DiagnosisFirewallDetection.cpp index 8049113..b3b9844 100644 --- a/src/core/DiagnosisFirewallDetection.cpp +++ b/src/core/DiagnosisFirewallDetection.cpp @@ -23,7 +23,7 @@ void DiagnosisFirewallDetection::checkIfAllInformationReady() void DiagnosisFirewallDetection::onFirstRuleDone(int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus); + Q_UNUSED(exitStatus) if (exitCode != 0) { @@ -75,7 +75,7 @@ void DiagnosisFirewallDetection::onFirstRuleError(QProcess::ProcessError pError) void DiagnosisFirewallDetection::onSecondRuleDone(int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus); + Q_UNUSED(exitStatus) if (exitCode != 0) { @@ -127,7 +127,7 @@ void DiagnosisFirewallDetection::onSecondRuleError(QProcess::ProcessError pError void DiagnosisFirewallDetection::onProfilesDone(int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus); + Q_UNUSED(exitStatus) if (exitCode != 0) { qDebug() << "Error while getting firewall profiles, error code:" << exitCode; @@ -183,7 +183,7 @@ void DiagnosisFirewallDetection::onProfilesError(QProcess::ProcessError pError) void DiagnosisFirewallDetection::onInstalledFirewallSoftwareDone(int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus); + Q_UNUSED(exitStatus) if (exitCode != 0) { @@ -231,8 +231,8 @@ void DiagnosisFirewallDetection::onInstalledFirewallSoftwareDone(int exitCode, Q QString hexString = QString::number(statusValue, 16); if (hexString.length() >= 4) { - enabled = hexString.right(4).left(2) == QStringLiteral("10"); - uptodate = hexString.right(2) == QStringLiteral("00"); + enabled = hexString.right(4).left(2) == QLatin1String("10"); + uptodate = hexString.right(2) == QLatin1String("00"); } else { @@ -370,7 +370,7 @@ void DiagnosisFirewallDetection::startDetection() } -FirewallSoftware::FirewallSoftware(QString pName, bool pEnabled, bool pUpToDate) +FirewallSoftware::FirewallSoftware(const QString& pName, bool pEnabled, bool pUpToDate) : mName(pName) , mEnabled(pEnabled) , mUpToDate(pUpToDate) @@ -378,7 +378,7 @@ FirewallSoftware::FirewallSoftware(QString pName, bool pEnabled, bool pUpToDate) } -FirewallProfile::FirewallProfile(QString pName, bool pEnabled) +FirewallProfile::FirewallProfile(const QString& pName, bool pEnabled) : mName(pName) , mEnabled(pEnabled) { diff --git a/src/core/DiagnosisFirewallDetection.h b/src/core/DiagnosisFirewallDetection.h index f69c51c..ecc0aea 100644 --- a/src/core/DiagnosisFirewallDetection.h +++ b/src/core/DiagnosisFirewallDetection.h @@ -23,7 +23,7 @@ class FirewallProfile const bool mEnabled; public: - FirewallProfile(QString pName, bool pEnabled); + FirewallProfile(const QString& pName, bool pEnabled); const QString& getName() const { @@ -47,7 +47,7 @@ class FirewallSoftware bool mUpToDate; public: - FirewallSoftware(QString pName, bool pEnabled, bool pUpToDate); + FirewallSoftware(const QString& pName, bool pEnabled, bool pUpToDate); const QString& getName() const { diff --git a/src/core/DiagnosisItem.cpp b/src/core/DiagnosisItem.cpp index f574051..c1f77e5 100644 --- a/src/core/DiagnosisItem.cpp +++ b/src/core/DiagnosisItem.cpp @@ -69,7 +69,7 @@ int DiagnosisItem::row() const } -int DiagnosisItem::getIndexOf(const DiagnosisItem* pChild) const +int DiagnosisItem::getIndexOf(const DiagnosisItem* const pChild) const { for (int i = 0; i < mChildren.length(); ++i) { diff --git a/src/core/DiagnosisItem.h b/src/core/DiagnosisItem.h index ff0970f..24dabbe 100644 --- a/src/core/DiagnosisItem.h +++ b/src/core/DiagnosisItem.h @@ -24,7 +24,7 @@ class DiagnosisItem QVector > mChildren; QSharedPointer mParent; - int getIndexOf(const DiagnosisItem* pChild) const; + int getIndexOf(const DiagnosisItem* const pChild) const; void setParent(const QSharedPointer& pParent); public: diff --git a/src/core/DiagnosisModel.cpp b/src/core/DiagnosisModel.cpp index d1195c8..ede4eb2 100644 --- a/src/core/DiagnosisModel.cpp +++ b/src/core/DiagnosisModel.cpp @@ -5,576 +5,180 @@ #include "DiagnosisModel.h" #include "AppSettings.h" +#include "BuildHelper.h" +#include "Env.h" +#include "GeneralSettings.h" #include "LanguageLoader.h" #include "RemoteServiceSettings.h" -#include "ScopeGuard.h" #include -#include + using namespace governikus; DiagnosisModel::DiagnosisModel(const QSharedPointer& pContext) : mContext(pContext) - , mRootItem(new DiagnosisItem(tr("Diagnosis data"))) - , mAppVersionItem(new DiagnosisItem(QCoreApplication::applicationName())) - , mOperatingSystemItem(new DiagnosisItem(tr("Operating system"))) - , mReaderItem(new DiagnosisItem(tr("Card reader"))) - , mPcScItem(new DiagnosisItem(tr("PC/SC"))) - , mPairedDevices(new DiagnosisItem(tr("Paired devices"))) - , mNetworkInterfaces(new DiagnosisItem(tr("Network interfaces"))) - , mNetworkConnectionTest(new DiagnosisItem(tr("Network connection test"))) - , mInstalledAntivirus(new DiagnosisItem(tr("Installed antivirus software"))) - , mWindowsFirewall(new DiagnosisItem(tr("Firewall"))) - , mTimestampItem(new DiagnosisItem(tr("Time of diagnosis"))) - , mAntivirusDetection() - , mFirewallDetection() - , mConnectionTest() { - mRootItem->addChild(mAppVersionItem); - initAppVersionInfo(); + reloadContent(); +} - mRootItem->addChild(mOperatingSystemItem); - mOperatingSystemItem->addChild(QSharedPointer::create(QSysInfo::prettyProductName())); - mOperatingSystemItem->addChild(QSharedPointer::create(QSysInfo::kernelVersion())); - mOperatingSystemItem->addChild(QSharedPointer::create(QSysInfo::currentCpuArchitecture())); - mRootItem->addChild(mReaderItem); - mReaderItem->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); - connect(mContext.data(), &DiagnosisContext::readerInfosChanged, this, &DiagnosisModel::onReaderInfosChanged); +DiagnosisModel::~DiagnosisModel() +{ + disconnectSignals(); +} - mRootItem->addChild(mPcScItem); - mPcScItem->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); + +QSharedPointer DiagnosisModel::createAusweisApp2Section() +{ + QSharedPointer aa2Section(new SectionModel); + + BuildHelper::processInformationHeader([aa2Section](const QString& pKey, const QString& pValue){ + aa2Section->addItem(pKey, pValue); + }); + + mTimestampItem = QSharedPointer::create(tr("Time of diagnosis"), tr("Initial diagnosis running, please wait.")); + aa2Section->addItem(mTimestampItem); + + return aa2Section; +} + + +void DiagnosisModel::createNetworkSection() +{ + mNetworkInterfaceSection = QSharedPointer::create(); + mNetworkConnectionSection = QSharedPointer::create(); + mCombinedNetworkSection = QSharedPointer::create(); +} + + +void DiagnosisModel::createCardReaderSection() +{ + mCombinedReaderSection = QSharedPointer::create(); + mPcscSection = QSharedPointer::create(); + mPcscSection->addTitleWithoutContent(tr("PC/SC information")); + mPcscSection->addItemWithoutTitle(tr("Diagnosis is running...")); + mCardReaderSection = QSharedPointer::create(); + mCardReaderSection->addTitleWithoutContent(tr("Card reader")); + mCardReaderSection->addItemWithoutTitle(tr("Diagnosis is running...")); + mRemoteDeviceSection = QSharedPointer::create(); + mRemoteDeviceSection->addTitleWithoutContent(tr("Paired remote devices")); + mRemoteDeviceSection->addItemWithoutTitle(tr("Diagnosis is running...")); +} + + +void DiagnosisModel::createAntiVirusAndFirewallSection() +{ + mAntivirusSection = QSharedPointer::create(); + mFirewallSection = QSharedPointer::create(); + mCombinedAntivirusFirewallSection = QSharedPointer::create(); + +#ifdef Q_OS_WIN + mAntivirusSection->addTitleWithoutContent(tr("Antivirus information")); + mAntivirusSection->addItemWithoutTitle(tr("Diagnosis is running...")); + + mFirewallSection->addTitleWithoutContent(tr("Firewall information")); + mFirewallSection->addItemWithoutTitle(tr("Diagnosis is running...")); +#else + mAntivirusSection->addItemWithoutTitle(tr("No Antivirus information available on this platform.")); + mFirewallSection->addItemWithoutTitle(tr("No Firewall information available on this platform.")); +#endif +} + + +void DiagnosisModel::emitDataChangedForSection(const QSharedPointer& pItem) const +{ + QSharedPointer sectionModel = pItem->mSection; + if (sectionModel) + { + sectionModel->emitDataChangedForItem(pItem); + } +} + + +void DiagnosisModel::connectSignals() +{ + const GeneralSettings& generalSettings = Env::getSingleton()->getGeneralSettings(); + connect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &DiagnosisModel::reloadContent); + connect(mContext.data(), &DiagnosisContext::timestampChanged, this, &DiagnosisModel::onTimestampChanged); + connect(mContext.data(), &DiagnosisContext::fireNetworkInfoChanged, this, &DiagnosisModel::onNetworkInfoChanged); + connect(&mConnectionTest, &DiagnosisConnectionTest::fireConnectionTestDone, this, &DiagnosisModel::onConnectionTestDone); connect(mContext.data(), &DiagnosisContext::pcscInfoChanged, this, &DiagnosisModel::onPcscInfoChanged); - - mRootItem->addChild(mPairedDevices); RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); connect(&settings, &RemoteServiceSettings::fireTrustedRemoteInfosChanged, this, &DiagnosisModel::onRemoteInfosChanged); - onRemoteInfosChanged(); + connect(mContext.data(), &DiagnosisContext::readerInfosChanged, this, &DiagnosisModel::onReaderInfosChanged); - mRootItem->addChild(mNetworkInterfaces); - connect(mContext.data(), &DiagnosisContext::fireNetworkInfoChanged, this, &DiagnosisModel::onNetworkInfoChanged); - - mRootItem->addChild(mNetworkConnectionTest); - mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); - connect(&mConnectionTest, &DiagnosisConnectionTest::fireConnectionTestDone, this, &DiagnosisModel::onConnectionTestDone); - mConnectionTest.startConnectionTest(); - - mRootItem->addChild(mInstalledAntivirus); - mRootItem->addChild(mWindowsFirewall); #ifdef Q_OS_WIN connect(&mAntivirusDetection, &DiagnosisAntivirusDetection::fireAntivirusInformationChanged, this, &DiagnosisModel::onAntivirusInformationChanged); connect(&mAntivirusDetection, &DiagnosisAntivirusDetection::fireDetectionFailed, this, &DiagnosisModel::onAntivirusDetectionFailed); - mAntivirusDetection.startInformationProcess(); - mInstalledAntivirus->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); - connect(&mFirewallDetection, &DiagnosisFirewallDetection::fireFirewallInformationReady, this, &DiagnosisModel::onFirewallInformationReady); connect(&mFirewallDetection, &DiagnosisFirewallDetection::fireDetectionFailed, this, &DiagnosisModel::onFirewallInformationFailed); - mFirewallDetection.startDetection(); - mWindowsFirewall->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); -#else - mInstalledAntivirus->addChild(QSharedPointer::create(tr("No Antivirus information available on this platform."))); - mWindowsFirewall->addChild(QSharedPointer::create(tr("No Firewall information available on this platform."))); #endif - - mRootItem->addChild(mTimestampItem); - connect(mContext.data(), &DiagnosisContext::timestampChanged, this, &DiagnosisModel::onTimestampChanged); - onTimestampChanged(); } -void DiagnosisModel::initAppVersionInfo() +void DiagnosisModel::disconnectSignals() { - const QStringList appVersion({ - QCoreApplication::applicationVersion(), - QCoreApplication::organizationName(), - QStringLiteral("Qt ") + QString::fromLatin1(qVersion()), - QSslSocket::sslLibraryVersionString() - }); - for (const auto& str : appVersion) - { - mAppVersionItem->addChild(QSharedPointer::create(str)); - } -} + const GeneralSettings& generalSettings = Env::getSingleton()->getGeneralSettings(); + disconnect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &DiagnosisModel::reloadContent); + disconnect(mContext.data(), &DiagnosisContext::timestampChanged, this, &DiagnosisModel::onTimestampChanged); + disconnect(mContext.data(), &DiagnosisContext::fireNetworkInfoChanged, this, &DiagnosisModel::onNetworkInfoChanged); + disconnect(&mConnectionTest, &DiagnosisConnectionTest::fireConnectionTestDone, this, &DiagnosisModel::onConnectionTestDone); + disconnect(mContext.data(), &DiagnosisContext::pcscInfoChanged, this, &DiagnosisModel::onPcscInfoChanged); + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + disconnect(&settings, &RemoteServiceSettings::fireTrustedRemoteInfosChanged, this, &DiagnosisModel::onRemoteInfosChanged); + disconnect(mContext.data(), &DiagnosisContext::readerInfosChanged, this, &DiagnosisModel::onReaderInfosChanged); -void DiagnosisModel::insertPcScComponentList(const QVector& pComponents, const QSharedPointer& pParentItem) -{ - if (pComponents.isEmpty()) - { - return; - } - - for (const DiagnosisContext::ComponentInfo& info : pComponents) - { - auto descriptionItem = QSharedPointer::create(info.getDescription()); - pParentItem->addChild(descriptionItem); - - auto companyItem = QSharedPointer::create(tr("Vendor: %1").arg(info.getManufacturer())); - pParentItem->addChild(companyItem); - - auto versionItem = QSharedPointer::create(tr("Version: %1").arg(info.getVersion())); - pParentItem->addChild(versionItem); - - auto pathItem = QSharedPointer::create(tr("File path: %1").arg(info.getPath())); - descriptionItem->addChild(pathItem); - } -} - - -void DiagnosisModel::removeChildItems(QModelIndex pIndex, QSharedPointer pParentItem) -{ - if (pParentItem->childCount() <= 0) - { - return; - } - - beginRemoveRows(pIndex, 0, pParentItem->childCount() - 1); - pParentItem->clearChildren(); - endRemoveRows(); -} - - -void DiagnosisModel::onReaderInfosChanged() -{ - auto itemModelIndex = index(2, 0); - removeChildItems(itemModelIndex, mReaderItem); - - const auto& readerInfos = mContext->getReaderInfos(); - if (readerInfos.isEmpty()) - { - beginInsertRows(itemModelIndex, 0, 0); - mReaderItem->addChild(QSharedPointer::create(tr("Not recognised"))); - endInsertRows(); - return; - } - - beginInsertRows(itemModelIndex, 0, readerInfos.size() - 1); - for (const ReaderInfo& info : readerInfos) - { - auto readerName = QSharedPointer::create(info.getName()); - mReaderItem->addChild(readerName); - - QString readerTypeString = info.isBasicReader() ? tr("Basic card reader") : tr("Standard / comfort card reader"); - auto readerType = QSharedPointer::create(tr("Type: %1").arg(readerTypeString)); - readerName->addChild(readerType); - - QString cardTypeString = info.getCardTypeString(); - auto cardType = QSharedPointer::create(tr("Card: %1").arg(cardTypeString)); - readerName->addChild(cardType); - - if (info.hasEidCard()) - { - auto retryCounter = QSharedPointer::create(tr("Retry counter: %1").arg(3 - info.getRetryCounter())); - readerName->addChild(retryCounter); - } - } - endInsertRows(); -} - - -void DiagnosisModel::onPcscInfoChanged() -{ - auto itemModelIndex = index(3, 0); - removeChildItems(itemModelIndex, mPcScItem); - - beginInsertRows(itemModelIndex, 0, 2); - auto pcscVersion = QSharedPointer::create(tr("Version: %1").arg(mContext->getPcscVersion())); - mPcScItem->addChild(pcscVersion); - - auto pcscComponents = QSharedPointer::create(tr("Components")); - mPcScItem->addChild(pcscComponents); - insertPcScComponentList(mContext->getPcscComponents(), pcscComponents); - - auto driverItem = QSharedPointer::create(tr("Driver")); - mPcScItem->addChild(driverItem); - insertPcScComponentList(mContext->getPcscDrivers(), driverItem); - endInsertRows(); -} - - -void DiagnosisModel::onRemoteInfosChanged() -{ - auto itemModelIndex = index(4, 0); - removeChildItems(itemModelIndex, mPairedDevices); - - const RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); - const auto& trustedCertificates = settings.getTrustedCertificates(); - - if (trustedCertificates.isEmpty()) - { - beginInsertRows(itemModelIndex, 0, 0); - mPairedDevices->addChild(QSharedPointer::create(tr("No devices paired"))); - endInsertRows(); - return; - } - - beginInsertRows(itemModelIndex, 0, trustedCertificates.size() - 1); - for (const auto& cert : trustedCertificates) - { - const auto& info = settings.getRemoteInfo(cert); - - if (!info.getFingerprint().isEmpty()) - { - const auto& deviceName = QSharedPointer::create(info.getName()); - mPairedDevices->addChild(deviceName); - - const auto& deviceFingerprint = QSharedPointer::create((tr("Certificate fingerprint: %1").arg(info.getFingerprint()))); - deviceName->addChild(deviceFingerprint); - - const QString& timestamp = LanguageLoader::getInstance().getUsedLocale().toString(info.getLastConnected(), tr("dd.MM.yyyy, hh:mm:ss")); - const auto& deviceLastConnected = QSharedPointer::create(tr("Last connection: %1").arg(timestamp)); - deviceName->addChild(deviceLastConnected); - } - else - { - const auto& deviceName = QSharedPointer::create(RemoteServiceSettings::generateFingerprint(cert)); - mPairedDevices->addChild(deviceName); - - const auto& deviceInfo = QSharedPointer::create(tr("No information found for this certificate")); - deviceName->addChild(deviceInfo); - } - } - endInsertRows(); -} - - -void DiagnosisModel::onTimestampChanged() -{ - auto itemModelIndex = index(9, 0); - removeChildItems(itemModelIndex, mTimestampItem); - - beginInsertRows(itemModelIndex, 0, 0); - QDateTime timestampValue = mContext->getTimestamp(); - if (!timestampValue.isValid()) - { - mTimestampItem->addChild(QSharedPointer::create(tr("Initial diagnosis running, please wait."))); - } - else - { - QString timestamp = LanguageLoader::getInstance().getUsedLocale().toString(timestampValue, tr("d. MMMM yyyy, hh:mm:ss AP")); - mTimestampItem->addChild(QSharedPointer::create(timestamp)); - } - endInsertRows(); -} - - -void DiagnosisModel::onNetworkInfoChanged() -{ - auto itemModelIndex = index(5, 0); - removeChildItems(itemModelIndex, mNetworkInterfaces); - - const auto& networkInterfaces = mContext->getNetworkInterfaces(); - beginInsertRows(itemModelIndex, 0, networkInterfaces.size() - 1); - for (const auto& interface : networkInterfaces) - { - const auto& interfaceName = QSharedPointer::create(interface.humanReadableName()); - mNetworkInterfaces->addChild(interfaceName); - - QString hardwareAddress = interface.hardwareAddress().isEmpty() ? tr("") : interface.hardwareAddress(); - interfaceName->addChild(QSharedPointer::create(tr("Hardware address: %1").arg(hardwareAddress))); - - const auto& addresses = interface.addressEntries(); - if (addresses.isEmpty()) - { - interfaceName->addChild(QSharedPointer::create(tr("No IP addresses assigned"))); - } - else - { - const auto& interfaceAddresses = QSharedPointer::create(tr("IP addresses")); - interfaceName->addChild(interfaceAddresses); - for (const auto& address : addresses) - { - const auto& ip = QSharedPointer::create(address.ip().toString()); - interfaceAddresses->addChild(ip); - } - } - } - endInsertRows(); -} - - -void DiagnosisModel::onConnectionTestDone() -{ - auto itemModelIndex = index(6, 0); - removeChildItems(itemModelIndex, mNetworkConnectionTest); - - if (mConnectionTest.getIsProxySet()) - { - beginInsertRows(itemModelIndex, 0, 2); - auto proxy = QSharedPointer::create(tr("Proxy")); - mNetworkConnectionTest->addChild(proxy); - - proxy->addChild(QSharedPointer::create(tr("Hostname: %1").arg(mConnectionTest.getProxyHostName()))); - proxy->addChild(QSharedPointer::create(tr("Port: %1").arg(mConnectionTest.getProxyPort()))); - proxy->addChild(QSharedPointer::create(tr("Type: %1").arg(mConnectionTest.getProxyType()))); - proxy->addChild(QSharedPointer::create(tr("Capabilities: %1").arg(mConnectionTest.getProxyCapabilities()))); - - if (mConnectionTest.getPingTestOnProxySuccessful()) - { - proxy->addChild(QSharedPointer::create(tr("Ping test to proxy: Successful"))); - } - else - { - proxy->addChild(QSharedPointer::create(tr("Ping test to proxy: Failed"))); - } - - if (mConnectionTest.getConnectionTestWithProxySuccessful()) - { - mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test with proxy: Successful"))); - } - else - { - mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test with proxy: Failed"))); - } - } - else - { - beginInsertRows(itemModelIndex, 0, 1); - mNetworkConnectionTest->addChild(QSharedPointer::create(tr("No Proxy Found"))); - } - - if (mConnectionTest.getConnectionTestWithoutProxySuccessful()) - { - mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test without proxy: Successful"))); - } - else - { - mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test without proxy: Failed"))); - } - endInsertRows(); -} - - -void DiagnosisModel::onAntivirusInformationChanged() -{ - auto itemModelIndex = index(7, 0); - removeChildItems(itemModelIndex, mInstalledAntivirus); - - const auto& antivirusInfos = mAntivirusDetection.getAntivirusInformations(); - if (antivirusInfos.isEmpty()) - { - beginInsertRows(itemModelIndex, 0, 0); - mInstalledAntivirus->addChild(QSharedPointer::create(tr("No Antivirus software detected."))); - } - else - { - beginInsertRows(itemModelIndex, 0, antivirusInfos.size() - 1); - for (const auto& info : antivirusInfos) - { - auto antivirusName = QSharedPointer::create(info->getDisplayName()); - mInstalledAntivirus->addChild(antivirusName); - - if (!info->getLastUpdate().isEmpty()) - { - antivirusName->addChild(QSharedPointer::create(tr("Last updated: %1").arg(info->getLastUpdate()))); - } - antivirusName->addChild(QSharedPointer::create(tr("Executable path: %1").arg(info->getExePath()))); - } - } - endInsertRows(); -} - - -void DiagnosisModel::onAntivirusDetectionFailed() -{ - auto itemModelIndex = index(7, 0); - removeChildItems(itemModelIndex, mInstalledAntivirus); - - beginInsertRows(itemModelIndex, 0, 0); - mInstalledAntivirus->addChild(QSharedPointer::create(tr("Antivirus detection failed."))); - endInsertRows(); -} - - -const QString DiagnosisModel::boolToString(bool pBoolean) -{ - return pBoolean ? tr("Yes") : tr("No"); -} - - -void DiagnosisModel::onFirewallInformationReady() -{ - auto itemModelIndex = index(8, 0); - removeChildItems(itemModelIndex, mWindowsFirewall); - - beginInsertRows(itemModelIndex, 0, 2); - auto installedFirewalls = mFirewallDetection.getDetectedFirewalls(); - if (installedFirewalls.isEmpty()) - { -#if defined(Q_OS_WIN) - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) - { - mWindowsFirewall->addChild(QSharedPointer::create(tr("Third party firewalls cannot be detected on Windows 7."))); - } - else - { -#else - { +#ifdef Q_OS_WIN + disconnect(&mAntivirusDetection, &DiagnosisAntivirusDetection::fireAntivirusInformationChanged, this, &DiagnosisModel::onAntivirusInformationChanged); + disconnect(&mAntivirusDetection, &DiagnosisAntivirusDetection::fireDetectionFailed, this, &DiagnosisModel::onAntivirusDetectionFailed); + disconnect(&mFirewallDetection, &DiagnosisFirewallDetection::fireFirewallInformationReady, this, &DiagnosisModel::onFirewallInformationReady); + disconnect(&mFirewallDetection, &DiagnosisFirewallDetection::fireDetectionFailed, this, &DiagnosisModel::onFirewallInformationFailed); #endif - mWindowsFirewall->addChild(QSharedPointer::create(tr("No third party firewalls detected"))); - } - } - else - { - auto thirdPartyFirewalls = QSharedPointer::create(tr("Firewalls from third party vendors")); - mWindowsFirewall->addChild(thirdPartyFirewalls); - for (const auto& firewall : installedFirewalls) - { - auto name = QSharedPointer::create(firewall->getName()); - thirdPartyFirewalls->addChild(name); - - QString enabled = boolToString(firewall->getEnabled()); - QString uptodate = boolToString(firewall->getUpToDate()); - name->addChild(QSharedPointer::create(tr("Enabled: %1").arg(enabled))); - name->addChild(QSharedPointer::create(tr("Up to date: %1").arg(uptodate))); - } - } - - auto firewallRules = QSharedPointer::create(tr("Windows firewall rules")); - mWindowsFirewall->addChild(firewallRules); - - QString firstRuleExists = boolToString(mFirewallDetection.getFirstRuleExists()); - QString firstRuleEnabled = boolToString(mFirewallDetection.getFirstRuleEnabled()); - auto outgoing = QSharedPointer::create(tr("Outgoing AusweisApp2 rule")); - firewallRules->addChild(outgoing); - outgoing->addChild(QSharedPointer::create(tr("Exists: %1").arg(firstRuleExists))); - outgoing->addChild(QSharedPointer::create(tr("Enabled: %1").arg(firstRuleEnabled))); - - QString secondRuleExists = boolToString(mFirewallDetection.getSecondRuleExists()); - QString secondRuleEnabled = boolToString(mFirewallDetection.getSecondRuleEnabled()); - auto incoming = QSharedPointer::create(tr("Incoming AusweisApp2 rule")); - firewallRules->addChild(incoming); - incoming->addChild(QSharedPointer::create(tr("Exists: %1").arg(secondRuleExists))); - incoming->addChild(QSharedPointer::create(tr("Enabled: %1").arg(secondRuleEnabled))); - - auto profiles = QSharedPointer::create(tr("Windows Firewall profiles")); - mWindowsFirewall->addChild(profiles); - - auto firewallProfiles = mFirewallDetection.getFirewallProfiles(); - for (const auto& profile : firewallProfiles) - { - auto name = QSharedPointer::create(profile->getName()); - profiles->addChild(name); - - QString enabled = boolToString(profile->getEnabled()); - name->addChild(QSharedPointer::create(tr("Enabled: %1").arg(enabled))); - } - profiles->addChild(QSharedPointer::create(tr("Warning: The current firewall status can be obscured by additional Group Policies on your system, often set by system administrators."))); - - endInsertRows(); -} - - -void DiagnosisModel::onFirewallInformationFailed() -{ - auto itemModelIndex = index(8, 0); - removeChildItems(itemModelIndex, mWindowsFirewall); - - beginInsertRows(itemModelIndex, 0, 0); - mWindowsFirewall->addChild(QSharedPointer::create(tr("An error occurred while trying to gather firewall information. Please check the log for more information."))); - endInsertRows(); } QVariant DiagnosisModel::data(const QModelIndex& pIndex, int pRole) const { - if (!pIndex.isValid()) + Q_UNUSED(pRole) + + const int row = pIndex.row(); + if (!pIndex.isValid() || row >= mSections.size()) { return QVariant(); } - if (pRole != Qt::DisplayRole) - { - return QVariant(); - } - - DiagnosisItem* item = static_cast(pIndex.internalPointer()); - - return item->getText(); -} - - -QModelIndex DiagnosisModel::index(int pRow, int pColumn, const QModelIndex& pParent) const -{ - if (!hasIndex(pRow, pColumn, pParent)) - { - return QModelIndex(); - } - - DiagnosisItem* parentItem; - - if (pParent.isValid()) - { - parentItem = static_cast(pParent.internalPointer()); - } - else - { - parentItem = mRootItem.data(); - } - - DiagnosisItem* childItem = parentItem->getChild(pRow).data(); - if (childItem) - { - return createIndex(pRow, pColumn, childItem); - } - else - { - return QModelIndex(); - } -} - - -QModelIndex DiagnosisModel::parent(const QModelIndex& pIndex) const -{ - if (!pIndex.isValid()) - { - return QModelIndex(); - } - - DiagnosisItem* childItem = static_cast(pIndex.internalPointer()); - if (childItem == nullptr) - { - return QModelIndex(); - } - - DiagnosisItem* parentItem = childItem->parentItem().data(); - - if (parentItem == mRootItem.data()) - { - return QModelIndex(); - } - - return createIndex(parentItem->row(), 0, parentItem); + return mSections.at(row).first; } int DiagnosisModel::rowCount(const QModelIndex& pParent) const { - DiagnosisItem* parentItem; - if (pParent.column() > 0) - { - return 0; - } - - if (pParent.isValid()) - { - parentItem = static_cast(pParent.internalPointer()); - } - else - { - parentItem = mRootItem.data(); - } - - return parentItem->childCount(); + Q_UNUSED(pParent) + return mSections.size(); } -int DiagnosisModel::columnCount(const QModelIndex& pParent) const +QString DiagnosisModel::getFirstSectionName() const { - Q_UNUSED(pParent); - return 1; + return mSections.first().first; +} + + +QAbstractListModel* DiagnosisModel::getSectionContent(const QString& pSection) +{ + for (const auto& pair : qAsConst(mSections)) + { + if (pair.first == pSection) + { + QSharedPointer sectionModel = pair.second; + return sectionModel.data(); + } + } + + return nullptr; } @@ -584,26 +188,539 @@ QDateTime DiagnosisModel::getCreationTime() const } +QString DiagnosisModel::getCreationTimeString() const +{ + return getCreationTime().toString(QStringLiteral("yyyy-MM-dd_HH-mm")); +} + + QString DiagnosisModel::getAsPlaintext() const { - QStringList modelPlaintext; - mRootItem->appendPlaintextContent(modelPlaintext); + #ifdef Q_OS_WIN - return modelPlaintext.join(QLatin1String("\r\n")); - + static const QString endl = QStringLiteral("\r\n"); #else - return modelPlaintext.join(QLatin1String("\n")); - + static const QString endl = QStringLiteral("\n"); #endif -} - -QVariant DiagnosisModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - Q_UNUSED(section); - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + QStringList modelPlaintext; + for (const auto& sectionPair : qAsConst(mSections)) { - return mRootItem->getText(); + modelPlaintext << sectionPair.first; + modelPlaintext << sectionPair.second->getAsPlaintext(QStringLiteral("\t")); + modelPlaintext << endl; } - return QVariant(); + + return modelPlaintext.join(endl); +} + + +QString DiagnosisModel::boolToString(bool pBoolean) +{ + return pBoolean ? tr("Yes") : tr("No"); +} + + +void DiagnosisModel::onTimestampChanged() +{ + QDateTime timestampValue = mContext->getTimestamp(); + if (!timestampValue.isValid()) + { + mTimestampItem->mContent = tr("Failed to retrieve date & time"); + } + else + { + QString timestamp = LanguageLoader::getInstance().getUsedLocale().toString(timestampValue, tr("d. MMMM yyyy, hh:mm:ss AP")); + mTimestampItem->mContent = timestamp; + } + + emitDataChangedForSection(mTimestampItem); +} + + +void DiagnosisModel::onNetworkInfoChanged() +{ + mNetworkInterfaceSection->removeAllItems(); + + const auto& networkInterfaces = mContext->getNetworkInterfaces(); + for (const auto& iface : networkInterfaces) + { + QStringList interfaceInfos; + QString hardwareAddress = iface.hardwareAddress().isEmpty() ? tr("") : iface.hardwareAddress(); + interfaceInfos << tr("Hardware address: %1").arg(hardwareAddress); + + const auto& addresses = iface.addressEntries(); + if (addresses.isEmpty()) + { + interfaceInfos << tr("No IP addresses assigned"); + } + else + { + for (const auto& address : addresses) + { + const auto& ip = address.ip(); + if (ip.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) + { + interfaceInfos << tr("IPv4 address: %1").arg(ip.toString()); + } + else if (ip.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv6Protocol) + { + interfaceInfos << tr("IPv6 address: %1").arg(ip.toString()); + } + else + { + interfaceInfos << tr("Unknown address: %1").arg(ip.toString()); + } + } + } + + mNetworkInterfaceSection->addItem(tr("Interface: \"%1\"").arg(iface.humanReadableName()), interfaceInfos.join(QStringLiteral("\n"))); + } + + mCombinedNetworkSection->replaceWithSections({mNetworkConnectionSection, mNetworkInterfaceSection}); +} + + +void DiagnosisModel::onConnectionTestDone() +{ + mNetworkConnectionSection->removeAllItems(); + QStringList proxyInfo; + + if (mConnectionTest.getIsProxySet()) + { + proxyInfo << tr("Hostname: %1").arg(mConnectionTest.getProxyHostName()); + proxyInfo << tr("Port: %1").arg(mConnectionTest.getProxyPort()); + proxyInfo << tr("Type: %1").arg(mConnectionTest.getProxyType()); + proxyInfo << tr("Capabilities: %1").arg(mConnectionTest.getProxyCapabilities()); + + if (mConnectionTest.getPingTestOnProxySuccessful()) + { + proxyInfo << tr("Ping test to proxy: Successful"); + } + else + { + proxyInfo << tr("Ping test to proxy: Failed"); + } + + if (mConnectionTest.getConnectionTestWithProxySuccessful()) + { + proxyInfo << tr("Connection test with proxy: Successful"); + } + else + { + proxyInfo << tr("Connection test with proxy: Failed"); + } + } + else + { + proxyInfo << tr("No proxy found"); + } + + if (mConnectionTest.getConnectionTestWithoutProxySuccessful()) + { + proxyInfo << tr("Connection test without proxy: Successful"); + } + else + { + proxyInfo << tr("Connection test without proxy: Failed"); + } + + mNetworkConnectionSection->addItem(tr("Proxy information"), proxyInfo.join(QStringLiteral("\n"))); + mCombinedNetworkSection->replaceWithSections({mNetworkConnectionSection, mNetworkInterfaceSection}); +} + + +void DiagnosisModel::onAntivirusInformationChanged() +{ + mAntivirusSection->removeAllItems(); + + const auto& antivirusInfos = mAntivirusDetection.getAntivirusInformations(); + if (antivirusInfos.isEmpty()) + { + mAntivirusSection->addItem(tr("Antivirus information"), tr("No Antivirus software detected.")); + } + else + { + mAntivirusSection->addTitleWithoutContent(tr("Antivirus information")); + + for (const auto& info : antivirusInfos) + { + QStringList avInfo; + if (!info->getLastUpdate().isEmpty()) + { + avInfo << tr("Last updated: %1").arg(info->getLastUpdate()); + } + avInfo << tr("Executable path: %1").arg(info->getExePath()); + auto antivirusName = info->getDisplayName(); + mAntivirusSection->addItem(antivirusName, avInfo.join(QStringLiteral("\n"))); + } + } + + mCombinedAntivirusFirewallSection->replaceWithSections({mAntivirusSection, mFirewallSection}); +} + + +void DiagnosisModel::onAntivirusDetectionFailed() +{ + mAntivirusSection->removeAllItems(); + mAntivirusSection->addItem(tr("Antivirus information"), tr("Antivirus detection failed.")); + mCombinedAntivirusFirewallSection->replaceWithSections({mAntivirusSection, mFirewallSection}); +} + + +void DiagnosisModel::onFirewallInformationReady() +{ + mFirewallSection->removeAllItems(); + + mFirewallSection->addTitleWithoutContent(tr("Firewall information")); + auto installedFirewalls = mFirewallDetection.getDetectedFirewalls(); + if (installedFirewalls.isEmpty()) + { +#if defined(Q_OS_WIN) + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) + { + mFirewallSection->addItemWithoutTitle(tr("Third party firewalls cannot be detected on Windows 7.")); + } + else + { +#else + { +#endif + mFirewallSection->addItemWithoutTitle(tr("No third party firewalls detected")); + } + } + else + { + QStringList firewallInfos; + for (const auto& firewall : installedFirewalls) + { + firewallInfos << firewall->getName(); + + QString enabled = boolToString(firewall->getEnabled()); + QString uptodate = boolToString(firewall->getUpToDate()); + firewallInfos << tr("Enabled: %1").arg(enabled); + firewallInfos << tr("Up to date: %1").arg(uptodate); + } + mFirewallSection->addItem(tr("Firewalls from third party vendors"), firewallInfos.join(QStringLiteral("\n"))); + } + + QStringList windowsFirewallSettings; + QString firstRuleExists = boolToString(mFirewallDetection.getFirstRuleExists()); + QString firstRuleEnabled = boolToString(mFirewallDetection.getFirstRuleEnabled()); + windowsFirewallSettings << tr("Outgoing AusweisApp2 rule"); + windowsFirewallSettings << tr("Exists: %1").arg(firstRuleExists); + windowsFirewallSettings << tr("Enabled: %1").arg(firstRuleEnabled); + + QString secondRuleExists = boolToString(mFirewallDetection.getSecondRuleExists()); + QString secondRuleEnabled = boolToString(mFirewallDetection.getSecondRuleEnabled()); + windowsFirewallSettings << tr("Incoming AusweisApp2 rule"); + windowsFirewallSettings << tr("Exists: %1").arg(secondRuleExists); + windowsFirewallSettings << tr("Enabled: %1").arg(secondRuleEnabled); + + mFirewallSection->addItem(tr("Windows firewall rules"), windowsFirewallSettings.join(QStringLiteral("\n"))); + + QStringList windowsFirewallProfiles; + auto firewallProfiles = mFirewallDetection.getFirewallProfiles(); + for (const auto& profile : firewallProfiles) + { + windowsFirewallProfiles << profile->getName(); + QString enabled = boolToString(profile->getEnabled()); + windowsFirewallProfiles << tr("Enabled: %1").arg(enabled); + } + + mFirewallSection->addItem(tr("Windows firewall profiles"), windowsFirewallProfiles.join(QStringLiteral("\n"))); + mFirewallSection->addItemWithoutTitle(tr("Warning: The current firewall status can be obscured by additional Group Policies on your system, often set by system administrators.")); + + mCombinedAntivirusFirewallSection->replaceWithSections({mAntivirusSection, mFirewallSection}); +} + + +void DiagnosisModel::onFirewallInformationFailed() +{ + mFirewallSection->removeAllItems(); + mFirewallSection->addItem(tr("Firewall information"), tr("An error occurred while trying to gather firewall information. Please check the log for more information.")); + mCombinedAntivirusFirewallSection->replaceWithSections({mAntivirusSection, mFirewallSection}); +} + + +void DiagnosisModel::onPcscInfoChanged() +{ + mPcscSection->removeAllItems(); + + mPcscSection->addTitleWithoutContent(tr("PC/SC information")); + mPcscSection->addItemWithoutTitle(tr("Version: %1").arg(mContext->getPcscVersion())); + + QStringList pcscInfo; + for (const auto& info : mContext->getPcscComponents()) + { + pcscInfo << info.getDescription(); + pcscInfo << tr("Vendor: %1").arg(info.getManufacturer()); + pcscInfo << tr("Version: %1").arg(info.getVersion()); + pcscInfo << tr("File path: %1").arg(info.getPath()); + } + if (!pcscInfo.empty()) + { + mPcscSection->addItem(tr("Components"), pcscInfo.join(QStringLiteral("\n"))); + } + + pcscInfo.clear(); + for (const auto& info : mContext->getPcscDrivers()) + { + pcscInfo << info.getDescription(); + pcscInfo << tr("Vendor: %1").arg(info.getManufacturer()); + pcscInfo << tr("Version: %1").arg(info.getVersion()); + pcscInfo << tr("File path: %1").arg(info.getPath()); + } + if (!pcscInfo.empty()) + { + mPcscSection->addItem(tr("Driver"), pcscInfo.join(QStringLiteral("\n"))); + } + + mCombinedReaderSection->replaceWithSections({mCardReaderSection, mRemoteDeviceSection, mPcscSection}); +} + + +void DiagnosisModel::onRemoteInfosChanged() +{ + mRemoteDeviceSection->removeAllItems(); + + const RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + const auto& trustedCertificates = settings.getTrustedCertificates(); + + mRemoteDeviceSection->addTitleWithoutContent(tr("Paired remote devices")); + + if (trustedCertificates.isEmpty()) + { + mRemoteDeviceSection->addItemWithoutTitle(tr("No devices paired.")); + } + + for (const auto& cert : trustedCertificates) + { + QStringList certInfo; + const auto& info = settings.getRemoteInfo(cert); + + if (!info.getFingerprint().isEmpty()) + { + certInfo << tr("Certificate fingerprint: %1").arg(info.getFingerprint()); + const QString& timestamp = LanguageLoader::getInstance().getUsedLocale().toString(info.getLastConnected(), tr("dd.MM.yyyy, hh:mm:ss")); + certInfo << tr("Last connection: %1").arg(timestamp); + + mRemoteDeviceSection->addItem(info.getName(), certInfo.join(QStringLiteral("\n"))); + } + else + { + mRemoteDeviceSection->addItem(RemoteServiceSettings::generateFingerprint(cert), tr("No information found for this certificate.")); + } + } + + mCombinedReaderSection->replaceWithSections({mCardReaderSection, mRemoteDeviceSection, mPcscSection}); +} + + +void DiagnosisModel::onReaderInfosChanged() +{ + mCardReaderSection->removeAllItems(); + + mCardReaderSection->addTitleWithoutContent(tr("Connected Card reader")); + + const auto& readerInfos = mContext->getReaderInfos(); + if (readerInfos.isEmpty()) + { + mCardReaderSection->addItemWithoutTitle(tr("No supported reader found.")); + } + + for (const auto& info : readerInfos) + { + QStringList infoList; + + QString readerType = info.isBasicReader() ? tr("Basic card reader") : tr("Standard / comfort card reader"); + infoList << tr("Type: %1").arg(readerType); + infoList << tr("Card: %1").arg(info.getCardTypeString()); + + if (info.hasEidCard()) + { + infoList << tr("Retry counter: %1").arg(info.getRetryCounter()); + } + + mCardReaderSection->addItem(info.getName(), infoList.join(QStringLiteral("\n"))); + } + + mCombinedReaderSection->replaceWithSections({mCardReaderSection, mRemoteDeviceSection, mPcscSection}); +} + + +void DiagnosisModel::reloadContent() +{ + disconnectSignals(); + + beginResetModel(); + mSections.clear(); + + mSections.append(qMakePair(QCoreApplication::applicationName(), createAusweisApp2Section())); + + createCardReaderSection(); + mCombinedReaderSection->replaceWithSections({mCardReaderSection, mRemoteDeviceSection, mPcscSection}); + mSections.append(qMakePair(tr("Card reader"), mCombinedReaderSection)); + + createNetworkSection(); + mCombinedNetworkSection->replaceWithSections({mNetworkConnectionSection, mNetworkInterfaceSection}); + mSections.append(qMakePair(tr("Network"), mCombinedNetworkSection)); + + createAntiVirusAndFirewallSection(); + mCombinedAntivirusFirewallSection->replaceWithSections({mAntivirusSection, mFirewallSection}); + mSections.append(qMakePair(tr("Antivirus and firewall"), mCombinedAntivirusFirewallSection)); + + onRemoteInfosChanged(); + mConnectionTest.startConnectionTest(); + +#ifdef Q_OS_WIN + mAntivirusDetection.startInformationProcess(); + mFirewallDetection.startDetection(); +#endif + + connectSignals(); + + endResetModel(); +} + + +SectionModel::SectionModel(QObject* pParent) + : QAbstractListModel(pParent) +{ + +} + + +QVariant SectionModel::data(const QModelIndex& pIndex, int pRole) const +{ + const int row = pIndex.row(); + if (!pIndex.isValid() || row >= mContentItems.size()) + { + return QVariant(); + } + + static const QString endl = QStringLiteral("\n"); + + QSharedPointer item = mContentItems.at(row); + switch (pRole) + { + case Qt::DisplayRole: + if (item->mTitle.isEmpty()) + { + return item->mContent; + } + else + { + return item->mTitle + endl + item->mContent; + } + + case TitleRole: + return item->mTitle; + + case ContentRole: + return item->mContent; + + default: + return QVariant(); + } +} + + +int SectionModel::rowCount(const QModelIndex& pParent) const +{ + Q_UNUSED(pParent) + return mContentItems.size(); +} + + +QHash SectionModel::roleNames() const +{ + QHash roles; + roles.insert(Qt::DisplayRole, QByteArrayLiteral("display")); + roles.insert(TitleRole, QByteArrayLiteral("title")); + roles.insert(ContentRole, QByteArrayLiteral("content")); + return roles; +} + + +void SectionModel::addItem(const QString& pTitle, const QString& pContent) +{ + addItem(QSharedPointer::create(pTitle, pContent)); +} + + +void SectionModel::addItem(const QSharedPointer& pContentItem) +{ + beginInsertRows(index(0), mContentItems.size(), mContentItems.size()); + mContentItems.append(pContentItem); + pContentItem->mSection = sharedFromThis(); + endInsertRows(); +} + + +void SectionModel::addItemWithoutTitle(const QString& pContent) +{ + addItem(QString(), pContent); +} + + +void SectionModel::addTitleWithoutContent(const QString& pTitle) +{ + addItem(pTitle, QString()); +} + + +void SectionModel::removeAllItems() +{ + if (!mContentItems.empty()) + { + beginRemoveRows(index(0), 0, mContentItems.size() - 1); + mContentItems.clear(); + endRemoveRows(); + } +} + + +void SectionModel::emitDataChangedForItem(const QSharedPointer& pItem) +{ + QModelIndex itemIndex = index(mContentItems.indexOf(pItem)); + Q_EMIT dataChanged(itemIndex, itemIndex); +} + + +void SectionModel::replaceWithSections(QVector > pSections) +{ + beginResetModel(); + removeAllItems(); + + for (const auto& section : qAsConst(pSections)) + { + const auto& sectionItems = section->mContentItems; + for (const auto& item : sectionItems) + { + addItem(item); + } + } + + endResetModel(); +} + + +QStringList SectionModel::getAsPlaintext(const QString& pPrependString) const +{ + QStringList sectionPlaintext; + for (const auto& item : qAsConst(mContentItems)) + { + if (!item->mTitle.isEmpty()) + { + sectionPlaintext << pPrependString + item->mTitle; + } + + if (!item->mContent.isEmpty()) + { + sectionPlaintext << pPrependString + item->mContent; + } + } + + return sectionPlaintext; } diff --git a/src/core/DiagnosisModel.h b/src/core/DiagnosisModel.h index 1dc1778..33b0868 100644 --- a/src/core/DiagnosisModel.h +++ b/src/core/DiagnosisModel.h @@ -10,8 +10,8 @@ #include "DiagnosisFirewallDetection.h" #include "DiagnosisItem.h" -#include -#include +#include +#include #include #include @@ -20,59 +20,114 @@ class test_DiagnosisModel; namespace governikus { +class SectionModel; +struct ContentItem +{ + ContentItem(const QString& pTitle, const QString& pContent) + : mTitle(pTitle) + , mContent(pContent) + { + } + + + QString mTitle; + QString mContent; + QSharedPointer mSection; +}; + + +class SectionModel + : public QAbstractListModel + , public QEnableSharedFromThis +{ + Q_OBJECT + + enum ContentRoles + { + TitleRole = Qt::UserRole + 1, + ContentRole + }; + + private: + friend class ::test_DiagnosisModel; + QVector > mContentItems; + + public: + explicit SectionModel(QObject* pParent = nullptr); + + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + int rowCount(const QModelIndex& pParent = QModelIndex()) const override; + QHash roleNames() const override; + + void addItem(const QString& pTitle, const QString& pContent); + void addItem(const QSharedPointer& pContentItem); + void addItemWithoutTitle(const QString& pContent); + void addTitleWithoutContent(const QString& pTitle); + void removeAllItems(); + void emitDataChangedForItem(const QSharedPointer& pItem); + void replaceWithSections(QVector > pSections); + QStringList getAsPlaintext(const QString& pPrependString = QString()) const; +}; + + class DiagnosisModel - : public QAbstractItemModel + : public QAbstractListModel { Q_OBJECT private: friend class ::test_DiagnosisModel; + QVector > > mSections; QSharedPointer mContext; - QSharedPointer mRootItem; - QSharedPointer mAppVersionItem; - QSharedPointer mOperatingSystemItem; - QSharedPointer mReaderItem; - QSharedPointer mPcScItem; - QSharedPointer mPairedDevices; - QSharedPointer mNetworkInterfaces; - QSharedPointer mNetworkConnectionTest; - QSharedPointer mInstalledAntivirus; - QSharedPointer mWindowsFirewall; - QSharedPointer mTimestampItem; DiagnosisAntivirusDetection mAntivirusDetection; DiagnosisFirewallDetection mFirewallDetection; DiagnosisConnectionTest mConnectionTest; + QSharedPointer mTimestampItem; + QSharedPointer mNetworkInterfaceSection; + QSharedPointer mNetworkConnectionSection; + QSharedPointer mCombinedNetworkSection; + QSharedPointer mCombinedAntivirusFirewallSection; + QSharedPointer mAntivirusSection; + QSharedPointer mFirewallSection; + QSharedPointer mCombinedReaderSection; + QSharedPointer mCardReaderSection; + QSharedPointer mPcscSection; + QSharedPointer mRemoteDeviceSection; - void initAppVersionInfo(); - void insertPcScComponentList(const QVector& pComponents, const QSharedPointer& pParentItem); - void removeChildItems(QModelIndex pIndex, QSharedPointer pParentItem); - static const QString boolToString(bool pBoolean); + QSharedPointer createAusweisApp2Section(); + void createNetworkSection(); + void createCardReaderSection(); + void createAntiVirusAndFirewallSection(); + void emitDataChangedForSection(const QSharedPointer& pItem) const; + void connectSignals(); + void disconnectSignals(); + + public: + explicit DiagnosisModel(const QSharedPointer& pContext); + ~DiagnosisModel() override; + + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + int rowCount(const QModelIndex& pParent = QModelIndex()) const override; + + QString getFirstSectionName() const; + QAbstractListModel* getSectionContent(const QString& pSection); + QDateTime getCreationTime() const; + Q_INVOKABLE QString getCreationTimeString() const; + QString getAsPlaintext() const; + QString boolToString(bool pBoolean); private Q_SLOTS: - void onReaderInfosChanged(); - void onPcscInfoChanged(); void onTimestampChanged(); void onNetworkInfoChanged(); - void onRemoteInfosChanged(); + void onConnectionTestDone(); void onAntivirusInformationChanged(); void onAntivirusDetectionFailed(); void onFirewallInformationReady(); void onFirewallInformationFailed(); - void onConnectionTestDone(); - - public: - explicit DiagnosisModel(const QSharedPointer& pContext); - - QVariant data(const QModelIndex& pIndex, int pRole) const override; - QModelIndex index(int pRow, int pColumn, - const QModelIndex& pParent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex& pIndex) const override; - int rowCount(const QModelIndex& pParent = QModelIndex()) const override; - int columnCount(const QModelIndex& pParent = QModelIndex()) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - - QDateTime getCreationTime() const; - QString getAsPlaintext() const; + void onPcscInfoChanged(); + void onRemoteInfosChanged(); + void onReaderInfosChanged(); + void reloadContent(); }; } // namespace governikus diff --git a/src/core/DiagnosisTreeModel.cpp b/src/core/DiagnosisTreeModel.cpp new file mode 100644 index 0000000..9e2982d --- /dev/null +++ b/src/core/DiagnosisTreeModel.cpp @@ -0,0 +1,614 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisTreeModel.h" + +#include "AppSettings.h" +#include "LanguageLoader.h" +#include "RemoteServiceSettings.h" + +#include +#include + +using namespace governikus; + + +DiagnosisTreeModel::DiagnosisTreeModel(const QSharedPointer& pContext) + : mContext(pContext) + , mRootItem(new DiagnosisItem(tr("Diagnosis data"))) + , mAppVersionItem(new DiagnosisItem(QCoreApplication::applicationName())) + , mOperatingSystemItem(new DiagnosisItem(tr("Operating system"))) + , mReaderItem(new DiagnosisItem(tr("Card reader"))) + , mPcScItem(new DiagnosisItem(tr("PC/SC"))) + , mPairedDevices(new DiagnosisItem(tr("Paired devices"))) + , mNetworkInterfaces(new DiagnosisItem(tr("Network interfaces"))) + , mNetworkConnectionTest(new DiagnosisItem(tr("Network connection test"))) + , mInstalledAntivirus(new DiagnosisItem(tr("Installed antivirus software"))) + , mWindowsFirewall(new DiagnosisItem(tr("Firewall"))) + , mTimestampItem(new DiagnosisItem(tr("Time of diagnosis"))) + , mAntivirusDetection() + , mFirewallDetection() + , mConnectionTest() +{ + mRootItem->addChild(mAppVersionItem); + initAppVersionInfo(); + + mRootItem->addChild(mOperatingSystemItem); + mOperatingSystemItem->addChild(QSharedPointer::create(QSysInfo::prettyProductName())); + mOperatingSystemItem->addChild(QSharedPointer::create(QSysInfo::kernelVersion())); + mOperatingSystemItem->addChild(QSharedPointer::create(QSysInfo::currentCpuArchitecture())); + + mRootItem->addChild(mReaderItem); + mReaderItem->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); + connect(mContext.data(), &DiagnosisContext::readerInfosChanged, this, &DiagnosisTreeModel::onReaderInfosChanged); + + mRootItem->addChild(mPcScItem); + mPcScItem->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); + connect(mContext.data(), &DiagnosisContext::pcscInfoChanged, this, &DiagnosisTreeModel::onPcscInfoChanged); + + mRootItem->addChild(mPairedDevices); + RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + connect(&settings, &RemoteServiceSettings::fireTrustedRemoteInfosChanged, this, &DiagnosisTreeModel::onRemoteInfosChanged); + onRemoteInfosChanged(); + + mRootItem->addChild(mNetworkInterfaces); + connect(mContext.data(), &DiagnosisContext::fireNetworkInfoChanged, this, &DiagnosisTreeModel::onNetworkInfoChanged); + + mRootItem->addChild(mNetworkConnectionTest); + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); + connect(&mConnectionTest, &DiagnosisConnectionTest::fireConnectionTestDone, this, &DiagnosisTreeModel::onConnectionTestDone); + mConnectionTest.startConnectionTest(); + + mRootItem->addChild(mInstalledAntivirus); + mRootItem->addChild(mWindowsFirewall); +#ifdef Q_OS_WIN + connect(&mAntivirusDetection, &DiagnosisAntivirusDetection::fireAntivirusInformationChanged, this, &DiagnosisTreeModel::onAntivirusInformationChanged); + connect(&mAntivirusDetection, &DiagnosisAntivirusDetection::fireDetectionFailed, this, &DiagnosisTreeModel::onAntivirusDetectionFailed); + mAntivirusDetection.startInformationProcess(); + mInstalledAntivirus->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); + + connect(&mFirewallDetection, &DiagnosisFirewallDetection::fireFirewallInformationReady, this, &DiagnosisTreeModel::onFirewallInformationReady); + connect(&mFirewallDetection, &DiagnosisFirewallDetection::fireDetectionFailed, this, &DiagnosisTreeModel::onFirewallInformationFailed); + mFirewallDetection.startDetection(); + mWindowsFirewall->addChild(QSharedPointer::create(tr("Diagnosis is running..."))); +#else + mInstalledAntivirus->addChild(QSharedPointer::create(tr("No Antivirus information available on this platform."))); + mWindowsFirewall->addChild(QSharedPointer::create(tr("No Firewall information available on this platform."))); +#endif + + mRootItem->addChild(mTimestampItem); + connect(mContext.data(), &DiagnosisContext::timestampChanged, this, &DiagnosisTreeModel::onTimestampChanged); + onTimestampChanged(); +} + + +void DiagnosisTreeModel::initAppVersionInfo() +{ + const QStringList appVersion({ + QCoreApplication::applicationVersion(), + QCoreApplication::organizationName(), + QStringLiteral("Qt ") + QString::fromLatin1(qVersion()), + QSslSocket::sslLibraryVersionString() + }); + for (const auto& str : appVersion) + { + mAppVersionItem->addChild(QSharedPointer::create(str)); + } +} + + +void DiagnosisTreeModel::insertPcScComponentList(const QVector& pComponents, const QSharedPointer& pParentItem) +{ + if (pComponents.isEmpty()) + { + return; + } + + for (const DiagnosisContext::ComponentInfo& info : pComponents) + { + auto descriptionItem = QSharedPointer::create(info.getDescription()); + pParentItem->addChild(descriptionItem); + + auto companyItem = QSharedPointer::create(tr("Vendor: %1").arg(info.getManufacturer())); + pParentItem->addChild(companyItem); + + auto versionItem = QSharedPointer::create(tr("Version: %1").arg(info.getVersion())); + pParentItem->addChild(versionItem); + + auto pathItem = QSharedPointer::create(tr("File path: %1").arg(info.getPath())); + descriptionItem->addChild(pathItem); + } +} + + +void DiagnosisTreeModel::removeChildItems(const QModelIndex& pIndex, const QSharedPointer& pParentItem) +{ + if (pParentItem->childCount() <= 0) + { + return; + } + + beginRemoveRows(pIndex, 0, pParentItem->childCount() - 1); + pParentItem->clearChildren(); + endRemoveRows(); +} + + +void DiagnosisTreeModel::onReaderInfosChanged() +{ + auto itemModelIndex = index(2, 0); + removeChildItems(itemModelIndex, mReaderItem); + + const auto& readerInfos = mContext->getReaderInfos(); + if (readerInfos.isEmpty()) + { + beginInsertRows(itemModelIndex, 0, 0); + mReaderItem->addChild(QSharedPointer::create(tr("Not recognised"))); + endInsertRows(); + return; + } + + beginInsertRows(itemModelIndex, 0, readerInfos.size() - 1); + for (const ReaderInfo& info : readerInfos) + { + auto readerName = QSharedPointer::create(info.getName()); + mReaderItem->addChild(readerName); + + QString readerTypeString = info.isBasicReader() ? tr("Basic card reader") : tr("Standard / comfort card reader"); + auto readerType = QSharedPointer::create(tr("Type: %1").arg(readerTypeString)); + readerName->addChild(readerType); + + QString cardTypeString = info.getCardTypeString(); + auto cardType = QSharedPointer::create(tr("Card: %1").arg(cardTypeString)); + readerName->addChild(cardType); + + if (info.hasEidCard()) + { + auto retryCounter = QSharedPointer::create(tr("Retry counter: %1").arg(info.getRetryCounter())); + readerName->addChild(retryCounter); + } + } + endInsertRows(); +} + + +void DiagnosisTreeModel::onPcscInfoChanged() +{ + auto itemModelIndex = index(3, 0); + removeChildItems(itemModelIndex, mPcScItem); + + beginInsertRows(itemModelIndex, 0, 2); + auto pcscVersion = QSharedPointer::create(tr("Version: %1").arg(mContext->getPcscVersion())); + mPcScItem->addChild(pcscVersion); + + auto pcscComponents = QSharedPointer::create(tr("Components")); + mPcScItem->addChild(pcscComponents); + insertPcScComponentList(mContext->getPcscComponents(), pcscComponents); + + auto driverItem = QSharedPointer::create(tr("Driver")); + mPcScItem->addChild(driverItem); + insertPcScComponentList(mContext->getPcscDrivers(), driverItem); + endInsertRows(); +} + + +void DiagnosisTreeModel::onRemoteInfosChanged() +{ + auto itemModelIndex = index(4, 0); + removeChildItems(itemModelIndex, mPairedDevices); + + const RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + const auto& trustedCertificates = settings.getTrustedCertificates(); + + if (trustedCertificates.isEmpty()) + { + beginInsertRows(itemModelIndex, 0, 0); + mPairedDevices->addChild(QSharedPointer::create(tr("No devices paired"))); + endInsertRows(); + return; + } + + beginInsertRows(itemModelIndex, 0, trustedCertificates.size() - 1); + for (const auto& cert : trustedCertificates) + { + const auto& info = settings.getRemoteInfo(cert); + + if (!info.getFingerprint().isEmpty()) + { + const auto& deviceName = QSharedPointer::create(info.getName()); + mPairedDevices->addChild(deviceName); + + const auto& deviceFingerprint = QSharedPointer::create((tr("Certificate fingerprint: %1").arg(info.getFingerprint()))); + deviceName->addChild(deviceFingerprint); + + const QString& timestamp = LanguageLoader::getInstance().getUsedLocale().toString(info.getLastConnected(), tr("dd.MM.yyyy, hh:mm:ss")); + const auto& deviceLastConnected = QSharedPointer::create(tr("Last connection: %1").arg(timestamp)); + deviceName->addChild(deviceLastConnected); + } + else + { + const auto& deviceName = QSharedPointer::create(RemoteServiceSettings::generateFingerprint(cert)); + mPairedDevices->addChild(deviceName); + + const auto& deviceInfo = QSharedPointer::create(tr("No information found for this certificate")); + deviceName->addChild(deviceInfo); + } + } + endInsertRows(); +} + + +void DiagnosisTreeModel::onTimestampChanged() +{ + auto itemModelIndex = index(9, 0); + removeChildItems(itemModelIndex, mTimestampItem); + + beginInsertRows(itemModelIndex, 0, 0); + QDateTime timestampValue = mContext->getTimestamp(); + if (!timestampValue.isValid()) + { + mTimestampItem->addChild(QSharedPointer::create(tr("Initial diagnosis running, please wait."))); + } + else + { + QString timestamp = LanguageLoader::getInstance().getUsedLocale().toString(timestampValue, tr("d. MMMM yyyy, hh:mm:ss AP")); + mTimestampItem->addChild(QSharedPointer::create(timestamp)); + } + endInsertRows(); +} + + +void DiagnosisTreeModel::onNetworkInfoChanged() +{ + auto itemModelIndex = index(5, 0); + removeChildItems(itemModelIndex, mNetworkInterfaces); + + const auto& networkInterfaces = mContext->getNetworkInterfaces(); + beginInsertRows(itemModelIndex, 0, networkInterfaces.size() - 1); + for (const auto& interface : networkInterfaces) + { + const auto& interfaceName = QSharedPointer::create(interface.humanReadableName()); + mNetworkInterfaces->addChild(interfaceName); + + QString hardwareAddress = interface.hardwareAddress().isEmpty() ? tr("") : interface.hardwareAddress(); + interfaceName->addChild(QSharedPointer::create(tr("Hardware address: %1").arg(hardwareAddress))); + + const auto& addresses = interface.addressEntries(); + if (addresses.isEmpty()) + { + interfaceName->addChild(QSharedPointer::create(tr("No IP addresses assigned"))); + } + else + { + const auto& interfaceAddresses = QSharedPointer::create(tr("IP addresses")); + interfaceName->addChild(interfaceAddresses); + for (const auto& address : addresses) + { + const auto& ip = QSharedPointer::create(address.ip().toString()); + interfaceAddresses->addChild(ip); + } + } + } + endInsertRows(); +} + + +void DiagnosisTreeModel::onConnectionTestDone() +{ + auto itemModelIndex = index(6, 0); + removeChildItems(itemModelIndex, mNetworkConnectionTest); + + if (mConnectionTest.getIsProxySet()) + { + beginInsertRows(itemModelIndex, 0, 2); + auto proxy = QSharedPointer::create(tr("Proxy")); + mNetworkConnectionTest->addChild(proxy); + + proxy->addChild(QSharedPointer::create(tr("Hostname: %1").arg(mConnectionTest.getProxyHostName()))); + proxy->addChild(QSharedPointer::create(tr("Port: %1").arg(mConnectionTest.getProxyPort()))); + proxy->addChild(QSharedPointer::create(tr("Type: %1").arg(mConnectionTest.getProxyType()))); + proxy->addChild(QSharedPointer::create(tr("Capabilities: %1").arg(mConnectionTest.getProxyCapabilities()))); + + if (mConnectionTest.getPingTestOnProxySuccessful()) + { + proxy->addChild(QSharedPointer::create(tr("Ping test to proxy: Successful"))); + } + else + { + proxy->addChild(QSharedPointer::create(tr("Ping test to proxy: Failed"))); + } + + if (mConnectionTest.getConnectionTestWithProxySuccessful()) + { + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test with proxy: Successful"))); + } + else + { + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test with proxy: Failed"))); + } + } + else + { + beginInsertRows(itemModelIndex, 0, 1); + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("No Proxy Found"))); + } + + if (mConnectionTest.getConnectionTestWithoutProxySuccessful()) + { + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test without proxy: Successful"))); + } + else + { + mNetworkConnectionTest->addChild(QSharedPointer::create(tr("Connection test without proxy: Failed"))); + } + endInsertRows(); +} + + +void DiagnosisTreeModel::onAntivirusInformationChanged() +{ + auto itemModelIndex = index(7, 0); + removeChildItems(itemModelIndex, mInstalledAntivirus); + + const auto& antivirusInfos = mAntivirusDetection.getAntivirusInformations(); + if (antivirusInfos.isEmpty()) + { + beginInsertRows(itemModelIndex, 0, 0); + mInstalledAntivirus->addChild(QSharedPointer::create(tr("No Antivirus software detected."))); + } + else + { + beginInsertRows(itemModelIndex, 0, antivirusInfos.size() - 1); + for (const auto& info : antivirusInfos) + { + auto antivirusName = QSharedPointer::create(info->getDisplayName()); + mInstalledAntivirus->addChild(antivirusName); + + if (!info->getLastUpdate().isEmpty()) + { + antivirusName->addChild(QSharedPointer::create(tr("Last updated: %1").arg(info->getLastUpdate()))); + } + antivirusName->addChild(QSharedPointer::create(tr("Executable path: %1").arg(info->getExePath()))); + } + } + endInsertRows(); +} + + +void DiagnosisTreeModel::onAntivirusDetectionFailed() +{ + auto itemModelIndex = index(7, 0); + removeChildItems(itemModelIndex, mInstalledAntivirus); + + beginInsertRows(itemModelIndex, 0, 0); + mInstalledAntivirus->addChild(QSharedPointer::create(tr("Antivirus detection failed."))); + endInsertRows(); +} + + +const QString DiagnosisTreeModel::boolToString(bool pBoolean) +{ + return pBoolean ? tr("Yes") : tr("No"); +} + + +void DiagnosisTreeModel::onFirewallInformationReady() +{ + auto itemModelIndex = index(8, 0); + removeChildItems(itemModelIndex, mWindowsFirewall); + + beginInsertRows(itemModelIndex, 0, 2); + auto installedFirewalls = mFirewallDetection.getDetectedFirewalls(); + if (installedFirewalls.isEmpty()) + { +#if defined(Q_OS_WIN) + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) + { + mWindowsFirewall->addChild(QSharedPointer::create(tr("Third party firewalls cannot be detected on Windows 7."))); + } + else + { +#else + { +#endif + mWindowsFirewall->addChild(QSharedPointer::create(tr("No third party firewalls detected"))); + } + } + else + { + auto thirdPartyFirewalls = QSharedPointer::create(tr("Firewalls from third party vendors")); + mWindowsFirewall->addChild(thirdPartyFirewalls); + for (const auto& firewall : installedFirewalls) + { + auto name = QSharedPointer::create(firewall->getName()); + thirdPartyFirewalls->addChild(name); + + QString enabled = boolToString(firewall->getEnabled()); + QString uptodate = boolToString(firewall->getUpToDate()); + name->addChild(QSharedPointer::create(tr("Enabled: %1").arg(enabled))); + name->addChild(QSharedPointer::create(tr("Up to date: %1").arg(uptodate))); + } + } + + auto firewallRules = QSharedPointer::create(tr("Windows firewall rules")); + mWindowsFirewall->addChild(firewallRules); + + QString firstRuleExists = boolToString(mFirewallDetection.getFirstRuleExists()); + QString firstRuleEnabled = boolToString(mFirewallDetection.getFirstRuleEnabled()); + auto outgoing = QSharedPointer::create(tr("Outgoing AusweisApp2 rule")); + firewallRules->addChild(outgoing); + outgoing->addChild(QSharedPointer::create(tr("Exists: %1").arg(firstRuleExists))); + outgoing->addChild(QSharedPointer::create(tr("Enabled: %1").arg(firstRuleEnabled))); + + QString secondRuleExists = boolToString(mFirewallDetection.getSecondRuleExists()); + QString secondRuleEnabled = boolToString(mFirewallDetection.getSecondRuleEnabled()); + auto incoming = QSharedPointer::create(tr("Incoming AusweisApp2 rule")); + firewallRules->addChild(incoming); + incoming->addChild(QSharedPointer::create(tr("Exists: %1").arg(secondRuleExists))); + incoming->addChild(QSharedPointer::create(tr("Enabled: %1").arg(secondRuleEnabled))); + + auto profiles = QSharedPointer::create(tr("Windows Firewall profiles")); + mWindowsFirewall->addChild(profiles); + + auto firewallProfiles = mFirewallDetection.getFirewallProfiles(); + for (const auto& profile : firewallProfiles) + { + auto name = QSharedPointer::create(profile->getName()); + profiles->addChild(name); + + QString enabled = boolToString(profile->getEnabled()); + name->addChild(QSharedPointer::create(tr("Enabled: %1").arg(enabled))); + } + profiles->addChild(QSharedPointer::create(tr("Warning: The current firewall status can be obscured by additional Group Policies on your system, often set by system administrators."))); + + endInsertRows(); +} + + +void DiagnosisTreeModel::onFirewallInformationFailed() +{ + auto itemModelIndex = index(8, 0); + removeChildItems(itemModelIndex, mWindowsFirewall); + + beginInsertRows(itemModelIndex, 0, 0); + mWindowsFirewall->addChild(QSharedPointer::create(tr("An error occurred while trying to gather firewall information. Please check the log for more information."))); + endInsertRows(); +} + + +QVariant DiagnosisTreeModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (!pIndex.isValid()) + { + return QVariant(); + } + + if (pRole != Qt::DisplayRole) + { + return QVariant(); + } + + auto* item = static_cast(pIndex.internalPointer()); + + return item->getText(); +} + + +QModelIndex DiagnosisTreeModel::index(int pRow, int pColumn, const QModelIndex& pParent) const +{ + if (!hasIndex(pRow, pColumn, pParent)) + { + return QModelIndex(); + } + + DiagnosisItem* parentItem; + + if (pParent.isValid()) + { + parentItem = static_cast(pParent.internalPointer()); + } + else + { + parentItem = mRootItem.data(); + } + + DiagnosisItem* childItem = parentItem->getChild(pRow).data(); + if (childItem) + { + return createIndex(pRow, pColumn, childItem); + } + else + { + return QModelIndex(); + } +} + + +QModelIndex DiagnosisTreeModel::parent(const QModelIndex& pIndex) const +{ + if (!pIndex.isValid()) + { + return QModelIndex(); + } + + auto* childItem = static_cast(pIndex.internalPointer()); + if (childItem == nullptr) + { + return QModelIndex(); + } + + DiagnosisItem* parentItem = childItem->parentItem().data(); + + if (parentItem == mRootItem.data()) + { + return QModelIndex(); + } + + return createIndex(parentItem->row(), 0, parentItem); +} + + +int DiagnosisTreeModel::rowCount(const QModelIndex& pParent) const +{ + DiagnosisItem* parentItem; + if (pParent.column() > 0) + { + return 0; + } + + if (pParent.isValid()) + { + parentItem = static_cast(pParent.internalPointer()); + } + else + { + parentItem = mRootItem.data(); + } + + return parentItem->childCount(); +} + + +int DiagnosisTreeModel::columnCount(const QModelIndex& pParent) const +{ + Q_UNUSED(pParent) + return 1; +} + + +QDateTime DiagnosisTreeModel::getCreationTime() const +{ + return mContext->getTimestamp(); +} + + +QString DiagnosisTreeModel::getCreationTimeString() const +{ + return getCreationTime().toString(QStringLiteral("yyyy-MM-dd_HH-mm")); +} + + +QString DiagnosisTreeModel::getAsPlaintext() const +{ + QStringList modelPlaintext; + mRootItem->appendPlaintextContent(modelPlaintext); +#ifdef Q_OS_WIN + return modelPlaintext.join(QLatin1String("\r\n")); + +#else + return modelPlaintext.join(QLatin1String("\n")); + +#endif +} + + +QVariant DiagnosisTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(section) + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + return mRootItem->getText(); + } + return QVariant(); +} diff --git a/src/core/DiagnosisTreeModel.h b/src/core/DiagnosisTreeModel.h new file mode 100644 index 0000000..5ed311b --- /dev/null +++ b/src/core/DiagnosisTreeModel.h @@ -0,0 +1,78 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/DiagnosisContext.h" +#include "DiagnosisAntivirusDetection.h" +#include "DiagnosisConnectionTest.h" +#include "DiagnosisFirewallDetection.h" +#include "DiagnosisItem.h" + +#include +#include +#include +#include + +class test_DiagnosisTreeModel; + +namespace governikus +{ + +class DiagnosisTreeModel + : public QAbstractItemModel +{ + Q_OBJECT + + private: + friend class ::test_DiagnosisTreeModel; + QSharedPointer mContext; + QSharedPointer mRootItem; + QSharedPointer mAppVersionItem; + QSharedPointer mOperatingSystemItem; + QSharedPointer mReaderItem; + QSharedPointer mPcScItem; + QSharedPointer mPairedDevices; + QSharedPointer mNetworkInterfaces; + QSharedPointer mNetworkConnectionTest; + QSharedPointer mInstalledAntivirus; + QSharedPointer mWindowsFirewall; + QSharedPointer mTimestampItem; + DiagnosisAntivirusDetection mAntivirusDetection; + DiagnosisFirewallDetection mFirewallDetection; + DiagnosisConnectionTest mConnectionTest; + + void initAppVersionInfo(); + void insertPcScComponentList(const QVector& pComponents, const QSharedPointer& pParentItem); + void removeChildItems(const QModelIndex& pIndex, const QSharedPointer& pParentItem); + static const QString boolToString(bool pBoolean); + + private Q_SLOTS: + void onReaderInfosChanged(); + void onPcscInfoChanged(); + void onTimestampChanged(); + void onNetworkInfoChanged(); + void onRemoteInfosChanged(); + void onAntivirusInformationChanged(); + void onAntivirusDetectionFailed(); + void onFirewallInformationReady(); + void onFirewallInformationFailed(); + void onConnectionTestDone(); + + public: + explicit DiagnosisTreeModel(const QSharedPointer& pContext); + + QVariant data(const QModelIndex& pIndex, int pRole) const override; + QModelIndex index(int pRow, int pColumn, const QModelIndex& pParent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& pIndex) const override; + int rowCount(const QModelIndex& pParent) const override; + int columnCount(const QModelIndex& pParent) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + QDateTime getCreationTime() const; + Q_INVOKABLE QString getCreationTimeString() const; + QString getAsPlaintext() const; +}; + +} // namespace governikus diff --git a/src/core/SelfAuthenticationData.cpp b/src/core/SelfAuthenticationData.cpp index a1955fe..1669b99 100644 --- a/src/core/SelfAuthenticationData.cpp +++ b/src/core/SelfAuthenticationData.cpp @@ -89,6 +89,7 @@ QString SelfAuthenticationData::SelfData::getValue(SelfAuthData pData) const return QStringLiteral("D"); } + //: INFO ALL_PLATFORMS The requested data is not stored on this chip's generation. return tr("This data has not been stored in this chip generation."); } @@ -128,7 +129,7 @@ bool SelfAuthenticationData::SelfData::parsePersonalData(const QJsonObject& pObj const auto& keys = pObject.keys(); for (const auto& entry : keys) { - const auto subvalue = [&pObject, &entry](const char* pValue){ + const auto subvalue = [&pObject, &entry](const char* const pValue){ return pObject.value(entry).toObject().value(QLatin1String(pValue)); }; @@ -220,38 +221,47 @@ SelfAuthenticationData::OrderedSelfData SelfAuthenticationData::SelfData::getOrd //fill layout with new data, see 18 Personalausweisgesetz (PAuswG) if (!getValue(SelfAuthData::FamilyNames).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Family name"), getValue(SelfAuthData::FamilyNames)); } if (!getValue(SelfAuthData::BirthName).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Birth name"), getValue(SelfAuthData::BirthName)); } if (!getValue(SelfAuthData::GivenNames).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Given name(s)"), getValue(SelfAuthData::GivenNames)); } if (!getValue(SelfAuthData::AcademicTitle).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Doctoral degree"), getValue(SelfAuthData::AcademicTitle)); } if (!getValue(SelfAuthData::DateOfBirth).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Date of birth"), formatDate(getValue(SelfAuthData::DateOfBirth))); } if (!getValue(SelfAuthData::PlaceOfBirth).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Place of birth"), getValue(SelfAuthData::PlaceOfBirth)); } if (!getValue(SelfAuthData::PlaceOfResidenceNoPlaceInfo).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Address"), getValue(SelfAuthData::PlaceOfResidenceNoPlaceInfo)); } if (!getValue(SelfAuthData::PlaceOfResidenceStreet).isNull()) { + //: LABEL ALL_PLATFORMS add(getValue(SelfAuthData::PlaceOfResidenceNoPlaceInfo).isNull() ? tr("Address") : QString(), getValue(SelfAuthData::PlaceOfResidenceStreet)); } if (!getValue(SelfAuthData::PlaceOfResidenceZipCode).isNull() || !getValue(SelfAuthData::PlaceOfResidenceCity).isNull()) { + //: LABEL ALL_PLATFORMS add(getValue(SelfAuthData::PlaceOfResidenceStreet).isNull() ? tr("Address") : QString(), getValue(SelfAuthData::PlaceOfResidenceZipCode) + QLatin1Char(' ') + getValue(SelfAuthData::PlaceOfResidenceCity)); } if (!getValue(SelfAuthData::PlaceOfResidenceCountry).isNull()) @@ -262,18 +272,22 @@ SelfAuthenticationData::OrderedSelfData SelfAuthenticationData::SelfData::getOrd const auto& documentType = getValue(SelfAuthData::DocumentType); if (!documentType.isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Document type"), documentType); } if (!getValue(SelfAuthData::Nationality).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Nationality"), getValue(SelfAuthData::Nationality)); } if (!getValue(SelfAuthData::ArtisticName).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Religious / artistic name"), getValue(SelfAuthData::ArtisticName)); } if (!getValue(SelfAuthData::IssuingState).isNull()) { + //: LABEL ALL_PLATFORMS add(tr("Issuing country"), getValue(SelfAuthData::IssuingState)); } @@ -286,6 +300,7 @@ SelfAuthenticationData::OrderedSelfData SelfAuthenticationData::SelfData::getOrd documentType == QLatin1String("AF") || documentType == QLatin1String("TA"))) { + //: LABEL ALL_PLATFORMS add(tr("Residence permit I"), getValue(SelfAuthData::ResidencePermitI)); } diff --git a/src/core/SelfAuthenticationData.h b/src/core/SelfAuthenticationData.h index 10cd641..10a8b6e 100644 --- a/src/core/SelfAuthenticationData.h +++ b/src/core/SelfAuthenticationData.h @@ -78,7 +78,7 @@ class SelfAuthenticationData QMap mOperationsAllowed; QMap mSelfAuthData; - SelfData(const QByteArray& pData); + explicit SelfData(const QByteArray& pData); QString getValue(SelfAuthData pData) const; OrderedSelfData getOrderedSelfInfo() const; }; @@ -86,7 +86,7 @@ class SelfAuthenticationData QSharedDataPointer d; public: - SelfAuthenticationData(const QByteArray& pData = QByteArray()); + explicit SelfAuthenticationData(const QByteArray& pData = QByteArray()); ~SelfAuthenticationData() = default; /** diff --git a/src/core/SignalHandler_bsd_linux_osx.cpp b/src/core/SignalHandler_bsd_linux_osx.cpp index 0a0c8f6..819315b 100644 --- a/src/core/SignalHandler_bsd_linux_osx.cpp +++ b/src/core/SignalHandler_bsd_linux_osx.cpp @@ -44,7 +44,10 @@ void SignalHandler::initUnix() void SignalHandler::sigHandler(int pSignal) { - ::write(cSignalSocketPair[0], &pSignal, sizeof(pSignal)); + if (::write(cSignalSocketPair[0], &pSignal, sizeof(pSignal)) != sizeof(pSignal)) + { + qCWarning(system) << "Cannot bind signal:" << pSignal; + } } diff --git a/src/core/TcToken.h b/src/core/TcToken.h index 71ffbf5..3336817 100644 --- a/src/core/TcToken.h +++ b/src/core/TcToken.h @@ -10,7 +10,6 @@ #include class test_TcToken; -class test_StateGenericSendReceive; namespace governikus { @@ -19,7 +18,6 @@ class TcToken { private: friend class ::test_TcToken; - friend class ::test_StateGenericSendReceive; bool mSchemaConform; QString mBinding; QString mPathSecurityProtocol; @@ -43,7 +41,7 @@ class TcToken QString readElementValue(QXmlStreamReader& pReader); public: - TcToken(const QByteArray& pData); + explicit TcToken(const QByteArray& pData); virtual ~TcToken(); /** diff --git a/src/core/context/AuthContext.cpp b/src/core/context/AuthContext.cpp index e089101..d3fa695 100644 --- a/src/core/context/AuthContext.cpp +++ b/src/core/context/AuthContext.cpp @@ -6,6 +6,7 @@ #include "asn1/Chat.h" #include "AppSettings.h" +#include "GeneralSettings.h" #include "paos/retrieve/DidAuthenticateEac1Parser.h" #include "SecureStorage.h" @@ -15,6 +16,8 @@ using namespace governikus; AuthContext::AuthContext(const QSharedPointer& pActivationContext) : WorkflowContext() + , mProgressValue(0) + , mProgressMessage() , mTcTokenNotFound(true) , mErrorReportedToServer(false) , mSkipRedirect(false) @@ -48,6 +51,35 @@ AuthContext::AuthContext(const QSharedPointer& pActivationCon , mCvcChainBuilderTest() , mSslSession() { + const auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + connect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &AuthContext::fireProgressChanged); +} + + +void AuthContext::setProgress(int pValue, const QString& pMessage) +{ + if (pValue != mProgressValue || pMessage != mProgressMessage) + { + mProgressValue = pValue; + mProgressMessage = pMessage; + + const auto& connection = getCardConnection(); + if (connection) + { + // Card interaction makes up about 80 % of the entire workflow's duration, + // "correct" the relative progress value accordingly. + if (pMessage.isEmpty()) + { + connection->setProgressMessage(QStringLiteral("%1 %").arg(1.25 * pValue)); + } + else + { + connection->setProgressMessage(QStringLiteral("%1\n%2 %").arg(pMessage).arg(1.25 * pValue)); + } + } + + Q_EMIT fireProgressChanged(); + } } @@ -292,9 +324,9 @@ void AuthContext::initCvcChainBuilder(const QVectorgetCvCertificates(); cvcs += pAdditionalCertificates; - const SecureStorage& secureStorage = SecureStorage::getInstance(); - mCvcChainBuilderProd = CVCertificateChainBuilder(cvcs + CVCertificate::fromHex(secureStorage.getCVRootCertificates(true)), true); - mCvcChainBuilderTest = CVCertificateChainBuilder(cvcs + CVCertificate::fromHex(secureStorage.getCVRootCertificates(false)), false); + const auto* secureStorage = Env::getSingleton(); + mCvcChainBuilderProd = CVCertificateChainBuilder(cvcs + CVCertificate::fromHex(secureStorage->getCVRootCertificates(true)), true); + mCvcChainBuilderTest = CVCertificateChainBuilder(cvcs + CVCertificate::fromHex(secureStorage->getCVRootCertificates(false)), false); } diff --git a/src/core/context/AuthContext.h b/src/core/context/AuthContext.h index 1379403..d06631e 100644 --- a/src/core/context/AuthContext.h +++ b/src/core/context/AuthContext.h @@ -37,6 +37,8 @@ class test_StateRedirectBrowser; class test_ChatModel; +class test_StatePreVerification; +class test_StateCertificateDescriptionCheck; namespace governikus { @@ -53,6 +55,8 @@ class AuthContext friend class ::test_StateCertificateDescriptionCheck; friend class ::test_ChatModel; + int mProgressValue; + QString mProgressMessage; bool mTcTokenNotFound; bool mErrorReportedToServer; bool mSkipRedirect; @@ -90,12 +94,13 @@ class AuthContext bool removeForbiddenAccessRights(QSet& pSet); Q_SIGNALS: + void fireProgressChanged(); void fireDidAuthenticateEac1Changed(); void fireAuthenticationDataChanged(); void fireEffectiveChatChanged(); public: - AuthContext(const QSharedPointer& pActivationContext); + explicit AuthContext(const QSharedPointer& pActivationContext); bool isErrorReportedToServer() const { @@ -109,6 +114,21 @@ class AuthContext } + int getProgressValue() const + { + return mProgressValue; + } + + + const QString getProgressMessage() const + { + return mProgressMessage; + } + + + void setProgress(int pValue, const QString& pMessage); + + bool isTcTokenNotFound() const { return mTcTokenNotFound; diff --git a/src/core/context/ChangePinContext.cpp b/src/core/context/ChangePinContext.cpp index acae9aa..9a32bc3 100644 --- a/src/core/context/ChangePinContext.cpp +++ b/src/core/context/ChangePinContext.cpp @@ -16,6 +16,17 @@ ChangePinContext::ChangePinContext(bool pRequestTransportPin) } +ChangePinContext::~ChangePinContext() +{ +#ifndef QT_NO_DEBUG + if (!QCoreApplication::applicationName().startsWith(QLatin1String("Test"))) + { + Q_ASSERT(getNewPin().isEmpty() && "PACE passwords must be cleared as soon as possible."); + } +#endif +} + + const QString& ChangePinContext::getNewPin() const { return mNewPin; diff --git a/src/core/context/ChangePinContext.h b/src/core/context/ChangePinContext.h index 583d7c0..405a225 100644 --- a/src/core/context/ChangePinContext.h +++ b/src/core/context/ChangePinContext.h @@ -22,6 +22,7 @@ class ChangePinContext public: explicit ChangePinContext(bool pRequestTransportPin = false); + virtual ~ChangePinContext() override; const QString& getNewPin() const; void setNewPin(const QString& pNewPin); diff --git a/src/core/context/RemoteServiceContext.cpp b/src/core/context/RemoteServiceContext.cpp index 02efa69..27b2bf4 100644 --- a/src/core/context/RemoteServiceContext.cpp +++ b/src/core/context/RemoteServiceContext.cpp @@ -9,6 +9,13 @@ using namespace governikus; + +void RemoteServiceContext::onMessageHandlerAdded(QSharedPointer pHandler) +{ + connect(pHandler.data(), &ServerMessageHandler::fireCardConnectionEstablished, this, &RemoteServiceContext::fireCardConnectionEstablished); +} + + RemoteServiceContext::RemoteServiceContext() : mRemoteServer(Env::create()) , mNewPin() @@ -17,6 +24,18 @@ RemoteServiceContext::RemoteServiceContext() , mModifyPinMessage() , mModifyPinMessageResponseApdu() { + connect(mRemoteServer.data(), &RemoteServer::fireMessageHandlerAdded, this, &RemoteServiceContext::onMessageHandlerAdded); +} + + +RemoteServiceContext::~RemoteServiceContext() +{ +#ifndef QT_NO_DEBUG + if (!QCoreApplication::applicationName().startsWith(QLatin1String("Test"))) + { + Q_ASSERT(getNewPin().isEmpty() && "PACE passwords must be cleared as soon as possible."); + } +#endif } @@ -61,7 +80,7 @@ const QSharedPointer& RemoteServiceContext::getEs } -void RemoteServiceContext::setEstablishPaceChannelOutput(EstablishPaceChannelOutput pEstablishPaceChannelOutput) +void RemoteServiceContext::setEstablishPaceChannelOutput(const EstablishPaceChannelOutput& pEstablishPaceChannelOutput) { mEstablishPaceChannelOutput = pEstablishPaceChannelOutput; } diff --git a/src/core/context/RemoteServiceContext.h b/src/core/context/RemoteServiceContext.h index 7118a75..22e7d3d 100644 --- a/src/core/context/RemoteServiceContext.h +++ b/src/core/context/RemoteServiceContext.h @@ -33,12 +33,17 @@ class RemoteServiceContext QSharedPointer mModifyPinMessage; ResponseApdu mModifyPinMessageResponseApdu; + public Q_SLOTS: + void onMessageHandlerAdded(QSharedPointer pHandler); + Q_SIGNALS: + void fireCardConnectionEstablished(const QSharedPointer& pConnection); void fireCancelPasswordRequest(); void fireEstablishPaceChannelMessageUpdated(const QSharedPointer& pMessage); public: RemoteServiceContext(); + virtual ~RemoteServiceContext() override; const QSharedPointer& getRemoteServer() const; bool isRunning() const; @@ -49,7 +54,7 @@ class RemoteServiceContext void setEstablishPaceChannelMessage(const QSharedPointer& pMessage); const QSharedPointer& getEstablishPaceChannelMessage() const; - void setEstablishPaceChannelOutput(EstablishPaceChannelOutput pEstablishPaceChannelOutput); + void setEstablishPaceChannelOutput(const EstablishPaceChannelOutput& pEstablishPaceChannelOutput); const EstablishPaceChannelOutput& getEstablishPaceChannelOutput() const; void setModifyPinMessage(const QSharedPointer& pMessage); diff --git a/src/core/context/WorkflowContext.cpp b/src/core/context/WorkflowContext.cpp index d0cf9e0..2a00700 100644 --- a/src/core/context/WorkflowContext.cpp +++ b/src/core/context/WorkflowContext.cpp @@ -21,7 +21,7 @@ WorkflowContext::WorkflowContext() : QObject() , mStateApproved(false) , mWorkflowKilled(false) - , mCurrentState() + , mCurrentState(QLatin1String("Initial")) , mReaderPlugInTypes() , mReaderName() , mCardConnection() @@ -43,11 +43,25 @@ WorkflowContext::WorkflowContext() , mWorkflowCancelled(false) , mCanAllowedMode(false) , mNextWorkflowPending(false) + , mCurrentReaderHasEidCardButInsufficientApduLength(false) { connect(this, &WorkflowContext::fireCancelWorkflow, this, &WorkflowContext::onWorkflowCancelled); } +WorkflowContext::~WorkflowContext() +{ +#ifndef QT_NO_DEBUG + if (!QCoreApplication::applicationName().startsWith(QLatin1String("Test"))) + { + Q_ASSERT(getPin().isEmpty() && "PACE passwords must be cleared as soon as possible."); + Q_ASSERT(getCan().isEmpty() && "PACE passwords must be cleared as soon as possible."); + Q_ASSERT(getPuk().isEmpty() && "PACE passwords must be cleared as soon as possible."); + } +#endif +} + + bool WorkflowContext::isErrorReportedToUser() const { return mErrorReportedToUser || mWorkflowKilled; @@ -283,6 +297,7 @@ PacePasswordId WorkflowContext::getEstablishPaceChannelType() const void WorkflowContext::setEstablishPaceChannelType(PacePasswordId pType) { mEstablishPaceChannelType = pType; + Q_EMIT firePasswordTypeChanged(); } @@ -315,11 +330,8 @@ CardReturnCode WorkflowContext::getLastPaceResult() const void WorkflowContext::setLastPaceResult(CardReturnCode pLastPaceResult) { mPaceResultReportedToUser = false; - if (mLastPaceResult != pLastPaceResult) - { - mLastPaceResult = pLastPaceResult; - Q_EMIT fireLastPaceResultChanged(); - } + mLastPaceResult = pLastPaceResult; + Q_EMIT firePaceResultUpdated(); } @@ -424,3 +436,19 @@ void WorkflowContext::setNextWorkflowPending(bool pNextWorkflowPending) Q_EMIT fireNextWorkflowPending(); } } + + +bool WorkflowContext::currentReaderHasEidCardButInsufficientApduLength() const +{ + return mCurrentReaderHasEidCardButInsufficientApduLength; +} + + +void WorkflowContext::setCurrentReaderHasEidCardButInsufficientApduLength(bool pState) +{ + if (pState != mCurrentReaderHasEidCardButInsufficientApduLength) + { + mCurrentReaderHasEidCardButInsufficientApduLength = pState; + Q_EMIT fireReaderInfoChanged(); + } +} diff --git a/src/core/context/WorkflowContext.h b/src/core/context/WorkflowContext.h index 52d39ca..3cee4b5 100644 --- a/src/core/context/WorkflowContext.h +++ b/src/core/context/WorkflowContext.h @@ -15,8 +15,6 @@ #include #include -class test_WorkflowContext; - namespace governikus { @@ -26,7 +24,6 @@ class WorkflowContext Q_OBJECT private: - friend class ::test_WorkflowContext; bool mStateApproved; bool mWorkflowKilled; QString mCurrentState; @@ -51,6 +48,7 @@ class WorkflowContext bool mWorkflowCancelled; bool mCanAllowedMode; bool mNextWorkflowPending; + bool mCurrentReaderHasEidCardButInsufficientApduLength; private Q_SLOTS: void onWorkflowCancelled(); @@ -59,22 +57,24 @@ class WorkflowContext void fireStateApprovedChanged(); void fireStateChanged(const QString& pNewState); void fireReaderPlugInTypesChanged(); + void fireReaderInfoChanged(); void fireReaderNameChanged(); void fireCardConnectionChanged(); void fireCanChanged(); void firePinChanged(); void firePukChanged(); - void fireLastPaceResultChanged(); + void firePaceResultUpdated(); void fireResultChanged(); void fireCanAllowedModeChanged(); + void firePasswordTypeChanged(); void fireCancelWorkflow(); - void fireAbortCardSelection(); void fireNextWorkflowPending(); public: WorkflowContext(); + virtual ~WorkflowContext(); bool isErrorReportedToUser() const; void setErrorReportedToUser(bool pErrorReportedToUser = true); @@ -149,6 +149,9 @@ class WorkflowContext bool hasNextWorkflowPending() const; void setNextWorkflowPending(bool pNextWorkflowPending); + + bool currentReaderHasEidCardButInsufficientApduLength() const; + void setCurrentReaderHasEidCardButInsufficientApduLength(bool pState); }; } // namespace governikus diff --git a/src/core/controller/AppController.cpp b/src/core/controller/AppController.cpp index e84ba88..76d4c39 100644 --- a/src/core/controller/AppController.cpp +++ b/src/core/controller/AppController.cpp @@ -94,6 +94,7 @@ AppController::AppController() , mActiveController() , mShutdownRunning(false) , mUiDomination(nullptr) + , mRestartApplication(false) { setObjectName(QStringLiteral("AppController")); @@ -101,8 +102,8 @@ AppController::AppController() QCoreApplication::instance()->installNativeEventFilter(this); #endif - connect(&Env::getSingleton()->getGeneralSettings(), &GeneralSettings::fireSettingsChanged, this, &AppController::onSettingsChanged, Qt::DirectConnection); - onSettingsChanged(); + connect(&Env::getSingleton()->getGeneralSettings(), &GeneralSettings::fireLanguageChanged, this, &AppController::onLanguageChanged, Qt::DirectConnection); + onLanguageChanged(); ResourceLoader::getInstance().init(); @@ -185,7 +186,7 @@ bool AppController::start() connect(this, &AppController::fireStarted, this, [this] { if (cShowUi) { - Q_EMIT fireShowUi(UiModule::DEFAULT); + Q_EMIT fireShowUi(UiModule::CURRENT); } }, Qt::QueuedConnection); @@ -195,6 +196,12 @@ bool AppController::start() } +bool AppController::shouldApplicationRestart() const +{ + return mRestartApplication; +} + + void AppController::onWorkflowFinished() { qDebug() << mActiveController->metaObject()->className() << "done"; @@ -285,7 +292,7 @@ void AppController::onChangePinRequested() void AppController::onSelfAuthenticationRequested() { - qDebug() << "Self authentication requested"; + qDebug() << "Self-authentication requested"; if (canStartNewAction()) { const auto& context = QSharedPointer::create(); @@ -304,11 +311,11 @@ void AppController::onAuthenticationRequest(const QSharedPointer activeContext = mActiveController->getContext(); Q_ASSERT(!activeContext.isNull()); - if (activeContext->isWorkflowFinished() && activeContext->getStatus().isNoError()) + if (activeContext->isWorkflowFinished()) { qDebug() << "Auto-approving the current state"; if (mWaitingRequest.isNull()) @@ -341,7 +348,7 @@ void AppController::onRemoteServiceRequested() } -void AppController::onSettingsChanged() +void AppController::onLanguageChanged() { LanguageLoader& languageLoader = LanguageLoader::getInstance(); const QLocale& newLocale = QLocale(Env::getSingleton()->getGeneralSettings().getLanguage()); @@ -410,7 +417,7 @@ void AppController::completeShutdown() handler->stop(); } - QTimer* timer = new QTimer(); + auto* timer = new QTimer(); static const int TIMER_INTERVAL = 50; timer->setInterval(TIMER_INTERVAL); connect(timer, &QTimer::timeout, this, [ = ](){ @@ -448,13 +455,13 @@ void AppController::onUILoaderShutdownComplete() void AppController::onUiDominationRequested(const UIPlugIn* pUi, const QString& pInformation) { bool accepted = false; - if (!mUiDomination && mCurrentAction == Action::NONE) + if (mUiDomination == nullptr && mCurrentAction == Action::NONE) { mUiDomination = pUi; accepted = true; } - qDebug() << pUi->metaObject()->className() << "requested ui domination:" << pInformation << "|" << accepted; + qDebug() << pUi->metaObject()->className() << "requested ui domination:" << pInformation << '|' << accepted; Q_EMIT fireUiDomination(pUi, pInformation, accepted); } @@ -469,6 +476,13 @@ void AppController::onUiDominationRelease() } +void AppController::onRestartApplicationRequested() +{ + mRestartApplication = true; + doShutdown(); +} + + void AppController::onUiPlugin(UIPlugIn* pPlugin) { qDebug() << "Register UI:" << pPlugin->metaObject()->className(); @@ -491,6 +505,7 @@ void AppController::onUiPlugin(UIPlugIn* pPlugin) connect(pPlugin, &UIPlugIn::fireChangePinRequest, this, &AppController::onChangePinRequested, Qt::QueuedConnection); connect(pPlugin, &UIPlugIn::fireSelfAuthenticationRequested, this, &AppController::onSelfAuthenticationRequested, Qt::QueuedConnection); connect(pPlugin, &UIPlugIn::fireRemoteServiceRequested, this, &AppController::onRemoteServiceRequested, Qt::QueuedConnection); + connect(pPlugin, &UIPlugIn::fireRestartApplicationRequested, this, &AppController::onRestartApplicationRequested, Qt::QueuedConnection); connect(pPlugin, &UIPlugIn::fireQuitApplicationRequest, this, &AppController::doShutdown); connect(pPlugin, &UIPlugIn::fireCloseReminderFinished, this, &AppController::onCloseReminderFinished); connect(pPlugin, &UIPlugIn::fireUiDominationRequest, this, &AppController::onUiDominationRequested); @@ -527,10 +542,10 @@ bool AppController::startNewWorkflow(Action pAction, const QSharedPointer #include +class test_AppController; namespace governikus { @@ -36,6 +37,7 @@ class AppController final Q_OBJECT private: + friend class ::test_AppController; Q_DISABLE_COPY(AppController) friend class SignalHandler; @@ -47,6 +49,7 @@ class AppController final QScopedPointer mActiveController; bool mShutdownRunning; const UIPlugIn* mUiDomination; + bool mRestartApplication; bool canStartNewAction(); void completeShutdown(); @@ -60,6 +63,8 @@ class AppController final bool start(); + bool shouldApplicationRestart() const; + Q_SIGNALS: void fireStarted(); void fireShutdown(); @@ -85,10 +90,11 @@ class AppController final void onSelfAuthenticationRequested(); void onAuthenticationRequest(const QSharedPointer& pActivationContext); void onRemoteServiceRequested(); - void onSettingsChanged(); + void onLanguageChanged(); void onUILoaderShutdownComplete(); void onUiDominationRequested(const UIPlugIn* pUi, const QString& pInformation); void onUiDominationRelease(); + void onRestartApplicationRequested(); private: template bool startNewWorkflow(Action pAction, const QSharedPointer& pContext); diff --git a/src/core/controller/AuthController.cpp b/src/core/controller/AuthController.cpp index b5bb4c4..a63a928 100644 --- a/src/core/controller/AuthController.cpp +++ b/src/core/controller/AuthController.cpp @@ -5,30 +5,15 @@ #include "controller/AuthController.h" #include "context/AuthContext.h" -#include "states/CompositeStatePace.h" -#include "states/CompositeStateProcessCvcsAndSetRights.h" +#include "states/CompositeStateTrustedChannel.h" #include "states/FinalState.h" #include "states/StateActivateStoreFeedbackDialog.h" #include "states/StateCheckRefreshAddress.h" -#include "states/StateCleanUpReaderManager.h" -#include "states/StateClearPacePasswords.h" -#include "states/StateDidAuthenticateEac1.h" -#include "states/StateDidAuthenticateEac2.h" -#include "states/StateDidList.h" -#include "states/StateEACAdditionalInputType.h" -#include "states/StateGenericSendReceive.h" -#include "states/StateGetTcToken.h" -#include "states/StateInitializeFramework.h" #include "states/StateParseTcTokenUrl.h" -#include "states/StateProcessCertificatesFromEac2.h" #include "states/StateProcessing.h" #include "states/StateRedirectBrowser.h" #include "states/StateSelectPasswordId.h" #include "states/StateSendWhitelistSurvey.h" -#include "states/StateStartPaos.h" -#include "states/StateStartPaosResponse.h" -#include "states/StateTransmit.h" -#include "states/StateUpdateRetryCounter.h" #include "states/StateWriteHistory.h" #include @@ -44,128 +29,23 @@ AuthController::AuthController(QSharedPointer pContext) auto sProcessing = addState(); mStateMachine.setInitialState(sProcessing); auto sParseTcTokenUrl = addState(); - auto sGetTcToken = addState(); - auto sStartPaos = addState(); - auto sSendStartPaos = addState(); - auto sInitializeFramework = addState(); - auto sSendInitializeFrameworkResponse = addState(); - auto sDidList = addState(); - auto sSendDidListResponse = addState(); - auto sProcessCvcsAndSetRights = new CompositeStateProcessCvcsAndSetRights(pContext); - mStateMachine.addState(sProcessCvcsAndSetRights); - auto sStatePace = new CompositeStatePace(pContext); - mStateMachine.addState(sStatePace); - auto sClearPacePasswords = addState(); - auto sDidAuthenticateEac1 = addState(); - auto sSendDidAuthenticateResponseEac1 = addState(); - auto sEacAdditionalInputType = addState(); - auto sSendDidAuthenticatResponseEacAdditionalInput = addState(); - auto sProcessCertificatesFromEac2 = addState(); - auto sDidAuthenticateEac2 = addState(); - auto sSendDidAuthenticateResponseEac2 = addState(); - auto sTransmit = addState(); - auto sSendTransmitResponse = addState(); - auto sSendDisconnectResponse = addState(); - auto sStartPaosResponse = addState(); + auto sTrustedChannel = new CompositeStateTrustedChannel(pContext); + mStateMachine.addState(sTrustedChannel); auto sCheckRefreshAddress = addState(); auto sActivateStoreFeedbackDialog = addState(); auto sWriteHistory = addState(); auto sRedirectBrowser = addState(); - auto sUpdateRetryCounterFinal = addState(); - auto sCleanUpReaderManager = addState(); auto sSendWhitelistSurvey = addState(); auto sFinal = addState(); sProcessing->addTransition(sProcessing, &AbstractState::fireContinue, sParseTcTokenUrl); - sProcessing->addTransition(sProcessing, &AbstractState::fireAbort, sCleanUpReaderManager); + sProcessing->addTransition(sProcessing, &AbstractState::fireAbort, sCheckRefreshAddress); - sParseTcTokenUrl->addTransition(sParseTcTokenUrl, &AbstractState::fireContinue, sGetTcToken); - sParseTcTokenUrl->addTransition(sParseTcTokenUrl, &AbstractState::fireAbort, sCleanUpReaderManager); + sParseTcTokenUrl->addTransition(sParseTcTokenUrl, &AbstractState::fireContinue, sTrustedChannel); + sParseTcTokenUrl->addTransition(sParseTcTokenUrl, &AbstractState::fireAbort, sCheckRefreshAddress); - sGetTcToken->addTransition(sGetTcToken, &AbstractState::fireContinue, sStartPaos); - sGetTcToken->addTransition(sGetTcToken, &AbstractState::fireAbort, sCleanUpReaderManager); - - sStartPaos->addTransition(sStartPaos, &AbstractState::fireContinue, sSendStartPaos); - sStartPaos->addTransition(sStartPaos, &AbstractState::fireAbort, sCleanUpReaderManager); - - sSendStartPaos->addTransition(sSendStartPaos, &AbstractState::fireContinue, sInitializeFramework); - sSendStartPaos->addTransition(sSendStartPaos, &AbstractState::fireAbort, sCleanUpReaderManager); - sSendStartPaos->addTransition(sSendStartPaos, &StateSendStartPaos::fireReceivedDidList, sDidList); - sSendStartPaos->addTransition(sSendStartPaos, &StateSendStartPaos::fireReceivedExtractCvcsFromEac1InputType, sProcessCvcsAndSetRights); - sSendStartPaos->addTransition(sSendStartPaos, &StateSendStartPaos::fireReceivedStartPaosResponse, sStartPaosResponse); - - sInitializeFramework->addTransition(sInitializeFramework, &AbstractState::fireContinue, sSendInitializeFrameworkResponse); - sInitializeFramework->addTransition(sInitializeFramework, &AbstractState::fireAbort, sSendInitializeFrameworkResponse); - - sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &AbstractState::fireContinue, sDidList); - sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &AbstractState::fireAbort, sCleanUpReaderManager); - sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &StateSendInitializeFrameworkResponse::fireReceivedExtractCvcsFromEac1InputType, sProcessCvcsAndSetRights); - sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &StateSendInitializeFrameworkResponse::fireReceivedStartPaosResponse, sStartPaosResponse); - - sDidList->addTransition(sDidList, &AbstractState::fireContinue, sSendDidListResponse); - sDidList->addTransition(sDidList, &AbstractState::fireAbort, sSendDidListResponse); - - sSendDidListResponse->addTransition(sSendDidListResponse, &AbstractState::fireContinue, sProcessCvcsAndSetRights); - sSendDidListResponse->addTransition(sSendDidListResponse, &AbstractState::fireAbort, sCleanUpReaderManager); - sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedDisconnect, sSendDisconnectResponse); - sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedStartPaosResponse, sStartPaosResponse); - - sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireContinue, sStatePace); - sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireAbort, sSendDidAuthenticateResponseEac1); - - sStatePace->addTransition(sStatePace, &CompositeStatePace::firePaceChannelEstablished, sClearPacePasswords); - sStatePace->addTransition(sStatePace, &CompositeStatePace::firePacePukEstablished, sStatePace); - sStatePace->addTransition(sStatePace, &CompositeStatePace::fireAbort, sSendDidAuthenticateResponseEac1); - - sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireContinue, sDidAuthenticateEac1); - sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireAbort, sDidAuthenticateEac1); - - sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireContinue, sSendDidAuthenticateResponseEac1); - sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &AbstractState::fireContinue, sEacAdditionalInputType); - sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &StateSendDIDAuthenticateResponseEAC1::fireReceivedDisconnect, sSendDisconnectResponse); - sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &StateSendDIDAuthenticateResponseEAC1::fireReceivedStartPaosResponse, sStartPaosResponse); - - sEacAdditionalInputType->addTransition(sEacAdditionalInputType, &AbstractState::fireContinue, sProcessCertificatesFromEac2); - sEacAdditionalInputType->addTransition(sEacAdditionalInputType, &AbstractState::fireAbort, sSendDidAuthenticatResponseEacAdditionalInput); - sEacAdditionalInputType->addTransition(sEacAdditionalInputType, &StateEACAdditionalInputType::fireSendDidAuthenticatResponse, sSendDidAuthenticatResponseEacAdditionalInput); - - sSendDidAuthenticatResponseEacAdditionalInput->addTransition(sSendDidAuthenticatResponseEacAdditionalInput, &AbstractState::fireContinue, sProcessCertificatesFromEac2); - sSendDidAuthenticatResponseEacAdditionalInput->addTransition(sSendDidAuthenticatResponseEacAdditionalInput, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - sSendDidAuthenticatResponseEacAdditionalInput->addTransition(sSendDidAuthenticatResponseEacAdditionalInput, &StateSendDIDAuthenticateResponseEACAdditionalInputType::fireReceivedStartPaosResponse, sStartPaosResponse); - - sProcessCertificatesFromEac2->addTransition(sProcessCertificatesFromEac2, &AbstractState::fireContinue, sDidAuthenticateEac2); - sProcessCertificatesFromEac2->addTransition(sProcessCertificatesFromEac2, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac2); - - sDidAuthenticateEac2->addTransition(sDidAuthenticateEac2, &AbstractState::fireContinue, sSendDidAuthenticateResponseEac2); - sDidAuthenticateEac2->addTransition(sDidAuthenticateEac2, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac2); - - sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &AbstractState::fireContinue, sTransmit); - sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &StateSendDIDAuthenticateResponseEAC2::fireReceivedDisconnect, sSendDisconnectResponse); - sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &StateSendDIDAuthenticateResponseEAC2::fireReceivedStartPaosResponse, sStartPaosResponse); - - sTransmit->addTransition(sTransmit, &AbstractState::fireContinue, sSendTransmitResponse); - sTransmit->addTransition(sTransmit, &AbstractState::fireAbort, sSendTransmitResponse); - - sSendTransmitResponse->addTransition(sSendTransmitResponse, &AbstractState::fireContinue, sTransmit); - sSendTransmitResponse->addTransition(sSendTransmitResponse, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - sSendTransmitResponse->addTransition(sSendTransmitResponse, &StateSendTransmitResponse::fireReceivedDisconnect, sSendDisconnectResponse); - sSendTransmitResponse->addTransition(sSendTransmitResponse, &StateSendTransmitResponse::fireReceivedStartPaosResponse, sStartPaosResponse); - - sSendDisconnectResponse->addTransition(sSendDisconnectResponse, &AbstractState::fireContinue, sStartPaosResponse); - sSendDisconnectResponse->addTransition(sSendDisconnectResponse, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - - sStartPaosResponse->addTransition(sStartPaosResponse, &AbstractState::fireContinue, sUpdateRetryCounterFinal); - sStartPaosResponse->addTransition(sStartPaosResponse, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - - sUpdateRetryCounterFinal->addTransition(sUpdateRetryCounterFinal, &AbstractState::fireContinue, sCleanUpReaderManager); - sUpdateRetryCounterFinal->addTransition(sUpdateRetryCounterFinal, &AbstractState::fireAbort, sCleanUpReaderManager); - - sCleanUpReaderManager->addTransition(sCleanUpReaderManager, &AbstractState::fireContinue, sCheckRefreshAddress); - sCleanUpReaderManager->addTransition(sCleanUpReaderManager, &AbstractState::fireAbort, sCheckRefreshAddress); + sTrustedChannel->addTransition(sTrustedChannel, &CompositeStateTrustedChannel::fireContinue, sCheckRefreshAddress); + sTrustedChannel->addTransition(sTrustedChannel, &CompositeStateTrustedChannel::fireAbort, sCheckRefreshAddress); sCheckRefreshAddress->addTransition(sCheckRefreshAddress, &AbstractState::fireContinue, sActivateStoreFeedbackDialog); sCheckRefreshAddress->addTransition(sCheckRefreshAddress, &AbstractState::fireAbort, sRedirectBrowser); diff --git a/src/core/controller/AuthController.h b/src/core/controller/AuthController.h index fc430dd..fb2025d 100644 --- a/src/core/controller/AuthController.h +++ b/src/core/controller/AuthController.h @@ -19,7 +19,7 @@ class AuthController Q_OBJECT public: - AuthController(QSharedPointer pContext); + explicit AuthController(QSharedPointer pContext); virtual ~AuthController() = default; }; diff --git a/src/core/controller/ChangePinController.cpp b/src/core/controller/ChangePinController.cpp index 9c767b4..49a84fb 100644 --- a/src/core/controller/ChangePinController.cpp +++ b/src/core/controller/ChangePinController.cpp @@ -39,7 +39,7 @@ ChangePinController::ChangePinController(QSharedPointer pConte mStateMachine.setInitialState(sStatePace); sStatePace->addTransition(sStatePace, &CompositeStatePace::firePaceChannelEstablished, sPrepareChangePin); - sStatePace->addTransition(sStatePace, &CompositeStatePace::firePacePukEstablished, sClearPacePasswords); + sStatePace->addTransition(sStatePace, &CompositeStatePace::firePacePukEstablished, sStatePace); sStatePace->addTransition(sStatePace, &CompositeStatePace::fireAbort, sClearPacePasswords); sPrepareChangePin->addTransition(sPrepareChangePin, &StatePrepareChangePin::fireContinue, sChangePin); diff --git a/src/core/controller/ChangePinController.h b/src/core/controller/ChangePinController.h index 39f6f92..d0950b1 100644 --- a/src/core/controller/ChangePinController.h +++ b/src/core/controller/ChangePinController.h @@ -20,7 +20,7 @@ class ChangePinController Q_OBJECT public: - ChangePinController(QSharedPointer pContext); + explicit ChangePinController(QSharedPointer pContext); virtual ~ChangePinController(); }; diff --git a/src/core/controller/DiagnosisController.cpp b/src/core/controller/DiagnosisController.cpp index 65b2989..e74cbb5 100644 --- a/src/core/controller/DiagnosisController.cpp +++ b/src/core/controller/DiagnosisController.cpp @@ -24,7 +24,7 @@ DiagnosisController::DiagnosisController(const QSharedPointer& connect(&mWatcherPcscInfo, &QFutureWatcher::finished, this, &DiagnosisController::onPcscInfoRetrieved); const auto& readerManager = Env::getSingleton(); - connect(readerManager, &ReaderManager::fireReaderEvent, this, &DiagnosisController::onFireReaderEvent); + connect(readerManager, &ReaderManager::fireReaderEvent, this, &DiagnosisController::onReaderEvent); } @@ -46,7 +46,7 @@ void DiagnosisController::run() if (!readerManager->isScanRunning()) { qCDebug(diagnosis) << "Scan not running, starting scan ourself and stop it afterwards."; - readerManager->startScanAll(true); + readerManager->startScanAll(false); mScanHasToBeStopped = true; } @@ -59,16 +59,7 @@ void DiagnosisController::onPcscInfoRetrieved() { auto info = mWatcherPcscInfo.future().result(); mContext->setPcscInfo(info.mPcscVersion, info.mPcscComponents, info.mPcscDrivers); - checkDone(); -} - - -void DiagnosisController::checkDone() -{ - if (mWatcherPcscInfo.isFinished()) - { - onFireReaderEvent(); - } + onReaderEvent(); } @@ -113,7 +104,7 @@ DiagnosisController::PcscInfo DiagnosisController::retrievePcscInfo() } -void DiagnosisController::onFireReaderEvent() +void DiagnosisController::onReaderEvent() { mContext->setReaderInfos(Env::getSingleton()->getReaderInfos()); mContext->setTimestamp(QDateTime::currentDateTime()); diff --git a/src/core/controller/DiagnosisController.h b/src/core/controller/DiagnosisController.h index be5f1ab..80c056f 100644 --- a/src/core/controller/DiagnosisController.h +++ b/src/core/controller/DiagnosisController.h @@ -32,8 +32,6 @@ class DiagnosisController QFutureWatcher mWatcherPcscInfo; bool mScanHasToBeStopped; - void checkDone(); - void collectInterfaceInformation(); static PcscInfo retrievePcscInfo(); @@ -48,7 +46,7 @@ class DiagnosisController private Q_SLOTS: void onPcscInfoRetrieved(); - void onFireReaderEvent(); + void onReaderEvent(); }; diff --git a/src/core/controller/DiagnosisController_generic.cpp b/src/core/controller/DiagnosisController_generic.cpp index 900f951..14ae371 100644 --- a/src/core/controller/DiagnosisController_generic.cpp +++ b/src/core/controller/DiagnosisController_generic.cpp @@ -14,6 +14,6 @@ using namespace governikus; void DiagnosisController::getPcscInfo(QVector& pComponents, QVector& pDrivers) { - Q_UNUSED(pComponents); - Q_UNUSED(pDrivers); + Q_UNUSED(pComponents) + Q_UNUSED(pDrivers) } diff --git a/src/core/controller/DiagnosisController_win.cpp b/src/core/controller/DiagnosisController_win.cpp index f44296f..d702361 100644 --- a/src/core/controller/DiagnosisController_win.cpp +++ b/src/core/controller/DiagnosisController_win.cpp @@ -18,7 +18,7 @@ using namespace governikus; #ifndef Q_OS_WINRT static QString getWindowsDirectoryPath() { - UINT length = GetSystemWindowsDirectory(nullptr, 0); + const auto length = static_cast(GetSystemWindowsDirectory(nullptr, 0)); if (length > 0) { QVector path(length + 1); @@ -49,7 +49,7 @@ static QString toAbsoluteWindowsDirectoryPath(const QString& pPath) } -static QString getWindowsFileVersionString(QByteArray& pVersionData, const char* pInfoName, int pLanguage, int pCodePage) +static QString getWindowsFileVersionString(QByteArray& pVersionData, const char* const pInfoName, int pLanguage, int pCodePage) { QString key = QString().sprintf("\\StringFileInfo\\%04x%04x\\%s", pLanguage, pCodePage, pInfoName); @@ -68,7 +68,7 @@ static void addWindowsComponentInfo(QVector& pC { std::wstring fileName = pFileName.toStdWString(); - DWORD infoSize = GetFileVersionInfoSize(fileName.data(), nullptr); + const auto infoSize = static_cast(GetFileVersionInfoSize(fileName.data(), nullptr)); if (infoSize == 0) { return; @@ -193,7 +193,7 @@ static QString getWindowsServiceDriverFileName(const QString& pServiceName) QueryServiceConfig(service, nullptr, 0, &configSize); if (configSize > 0) { - QByteArray serviceConfigBuffer(configSize, 0); + QByteArray serviceConfigBuffer(static_cast(configSize), 0); QUERY_SERVICE_CONFIG* serviceConfig = reinterpret_cast(serviceConfigBuffer.data()); if (QueryServiceConfig(service, serviceConfig, configSize, &configSize)) { diff --git a/src/core/controller/RemoteServiceController.h b/src/core/controller/RemoteServiceController.h index 5242e09..8560626 100644 --- a/src/core/controller/RemoteServiceController.h +++ b/src/core/controller/RemoteServiceController.h @@ -19,7 +19,7 @@ class RemoteServiceController Q_OBJECT public: - RemoteServiceController(QSharedPointer pContext); + explicit RemoteServiceController(QSharedPointer pContext); virtual ~RemoteServiceController(); }; diff --git a/src/core/controller/SelfAuthController.cpp b/src/core/controller/SelfAuthController.cpp index 8f73fdd..5131fdc 100644 --- a/src/core/controller/SelfAuthController.cpp +++ b/src/core/controller/SelfAuthController.cpp @@ -5,30 +5,15 @@ #include "controller/SelfAuthController.h" #include "context/SelfAuthContext.h" -#include "states/CompositeStatePace.h" -#include "states/CompositeStateProcessCvcsAndSetRights.h" +#include "states/CompositeStateTrustedChannel.h" #include "states/FinalState.h" #include "states/StateActivateStoreFeedbackDialog.h" #include "states/StateCheckError.h" #include "states/StateCheckRefreshAddress.h" -#include "states/StateCleanUpReaderManager.h" -#include "states/StateClearPacePasswords.h" -#include "states/StateDidAuthenticateEac1.h" -#include "states/StateDidAuthenticateEac2.h" -#include "states/StateDidList.h" -#include "states/StateEACAdditionalInputType.h" -#include "states/StateGenericSendReceive.h" #include "states/StateGetSelfAuthenticationData.h" -#include "states/StateGetTcToken.h" -#include "states/StateInitializeFramework.h" #include "states/StateLoadTcTokenUrl.h" -#include "states/StateProcessCertificatesFromEac2.h" #include "states/StateSendWhitelistSurvey.h" #include "states/StateShowSelfInfo.h" -#include "states/StateStartPaos.h" -#include "states/StateStartPaosResponse.h" -#include "states/StateTransmit.h" -#include "states/StateUpdateRetryCounter.h" #include "states/StateWriteHistory.h" #include @@ -43,127 +28,22 @@ SelfAuthController::SelfAuthController(QSharedPointer pContext) { auto sLoadTcTokenUrl = addState(); mStateMachine.setInitialState(sLoadTcTokenUrl); - auto sGetTcToken = addState(); - auto sStartPaos = addState(); - auto sSendStartPaos = addState(); - auto sInitializeFramework = addState(); - auto sSendInitializeFrameworkResponse = addState(); - auto sDidList = addState(); - auto sSendDidListResponse = addState(); - auto sProcessCvcsAndSetRights = new CompositeStateProcessCvcsAndSetRights(pContext); - mStateMachine.addState(sProcessCvcsAndSetRights); - auto sStatePace = new CompositeStatePace(pContext); - mStateMachine.addState(sStatePace); - auto sClearPacePasswords = addState(); - auto sDidAuthenticateEac1 = addState(); - auto sSendDidAuthenticateResponseEac1 = addState(); - auto sEacAdditionalInputType = addState(); - auto sSendDidAuthenticatResponseEacAdditionalInput = addState(); - auto sProcessCertificatesFromEac2 = addState(); - auto sDidAuthenticateEac2 = addState(); - auto sSendDidAuthenticateResponseEac2 = addState(); - auto sTransmit = addState(); - auto sSendTransmitResponse = addState(); - auto sSendDisconnectResponse = addState(); - auto sStartPaosResponse = addState(); + auto sTrustedChannel = new CompositeStateTrustedChannel(pContext); + mStateMachine.addState(sTrustedChannel); auto sCheckErrorEpilogue = addState(); auto sCheckRefreshAddress = addState(); auto sActivateStoreFeedbackDialog = addState(); auto sWriteHistory = addState(); auto sSendWhitelistSurvey = addState(); auto sGetSelfAuthenticationData = addState(); - auto sUpdateRetryCounterFinal = addState(); - auto sCleanUpReaderManager = addState(); auto sShowSelfInfo = addState(); auto sFinal = addState(); - sLoadTcTokenUrl->addTransition(sLoadTcTokenUrl, &AbstractState::fireContinue, sGetTcToken); - sLoadTcTokenUrl->addTransition(sLoadTcTokenUrl, &AbstractState::fireAbort, sCleanUpReaderManager); + sLoadTcTokenUrl->addTransition(sLoadTcTokenUrl, &AbstractState::fireContinue, sTrustedChannel); + sLoadTcTokenUrl->addTransition(sLoadTcTokenUrl, &AbstractState::fireAbort, sCheckRefreshAddress); - sGetTcToken->addTransition(sGetTcToken, &AbstractState::fireContinue, sStartPaos); - sGetTcToken->addTransition(sGetTcToken, &AbstractState::fireAbort, sCleanUpReaderManager); - - sStartPaos->addTransition(sStartPaos, &AbstractState::fireContinue, sSendStartPaos); - sStartPaos->addTransition(sStartPaos, &AbstractState::fireAbort, sCleanUpReaderManager); - - sSendStartPaos->addTransition(sSendStartPaos, &AbstractState::fireContinue, sInitializeFramework); - sSendStartPaos->addTransition(sSendStartPaos, &AbstractState::fireAbort, sCleanUpReaderManager); - sSendStartPaos->addTransition(sSendStartPaos, &StateSendStartPaos::fireReceivedDidList, sDidList); - sSendStartPaos->addTransition(sSendStartPaos, &StateSendStartPaos::fireReceivedExtractCvcsFromEac1InputType, sProcessCvcsAndSetRights); - sSendStartPaos->addTransition(sSendStartPaos, &StateSendStartPaos::fireReceivedStartPaosResponse, sStartPaosResponse); - - sInitializeFramework->addTransition(sInitializeFramework, &AbstractState::fireContinue, sSendInitializeFrameworkResponse); - sInitializeFramework->addTransition(sInitializeFramework, &AbstractState::fireAbort, sSendInitializeFrameworkResponse); - - sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &AbstractState::fireContinue, sDidList); - sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &AbstractState::fireAbort, sCleanUpReaderManager); - sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &StateSendInitializeFrameworkResponse::fireReceivedExtractCvcsFromEac1InputType, sProcessCvcsAndSetRights); - sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &StateSendInitializeFrameworkResponse::fireReceivedStartPaosResponse, sStartPaosResponse); - - sDidList->addTransition(sDidList, &AbstractState::fireContinue, sSendDidListResponse); - sDidList->addTransition(sDidList, &AbstractState::fireAbort, sSendDidListResponse); - - sSendDidListResponse->addTransition(sSendDidListResponse, &AbstractState::fireContinue, sProcessCvcsAndSetRights); - sSendDidListResponse->addTransition(sSendDidListResponse, &AbstractState::fireAbort, sCleanUpReaderManager); - sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedDisconnect, sSendDisconnectResponse); - sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedStartPaosResponse, sStartPaosResponse); - - sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireContinue, sStatePace); - sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireAbort, sSendDidAuthenticateResponseEac1); - - sStatePace->addTransition(sStatePace, &CompositeStatePace::firePaceChannelEstablished, sClearPacePasswords); - sStatePace->addTransition(sStatePace, &CompositeStatePace::firePacePukEstablished, sStatePace); - sStatePace->addTransition(sStatePace, &CompositeStatePace::fireAbort, sSendDidAuthenticateResponseEac1); - - sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireContinue, sDidAuthenticateEac1); - sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireAbort, sDidAuthenticateEac1); - - sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireContinue, sSendDidAuthenticateResponseEac1); - sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); - - sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &AbstractState::fireContinue, sEacAdditionalInputType); - sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &StateSendDIDAuthenticateResponseEAC1::fireReceivedDisconnect, sSendDisconnectResponse); - sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &StateSendDIDAuthenticateResponseEAC1::fireReceivedStartPaosResponse, sStartPaosResponse); - - sEacAdditionalInputType->addTransition(sEacAdditionalInputType, &AbstractState::fireContinue, sProcessCertificatesFromEac2); - sEacAdditionalInputType->addTransition(sEacAdditionalInputType, &AbstractState::fireAbort, sSendDidAuthenticatResponseEacAdditionalInput); - sEacAdditionalInputType->addTransition(sEacAdditionalInputType, &StateEACAdditionalInputType::fireSendDidAuthenticatResponse, sSendDidAuthenticatResponseEacAdditionalInput); - - sSendDidAuthenticatResponseEacAdditionalInput->addTransition(sSendDidAuthenticatResponseEacAdditionalInput, &AbstractState::fireContinue, sProcessCertificatesFromEac2); - sSendDidAuthenticatResponseEacAdditionalInput->addTransition(sSendDidAuthenticatResponseEacAdditionalInput, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - sSendDidAuthenticatResponseEacAdditionalInput->addTransition(sSendDidAuthenticatResponseEacAdditionalInput, &StateSendDIDAuthenticateResponseEACAdditionalInputType::fireReceivedStartPaosResponse, sStartPaosResponse); - - sProcessCertificatesFromEac2->addTransition(sProcessCertificatesFromEac2, &AbstractState::fireContinue, sDidAuthenticateEac2); - sProcessCertificatesFromEac2->addTransition(sProcessCertificatesFromEac2, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac2); - - sDidAuthenticateEac2->addTransition(sDidAuthenticateEac2, &AbstractState::fireContinue, sSendDidAuthenticateResponseEac2); - sDidAuthenticateEac2->addTransition(sDidAuthenticateEac2, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac2); - - sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &AbstractState::fireContinue, sTransmit); - sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &StateSendDIDAuthenticateResponseEAC2::fireReceivedDisconnect, sSendDisconnectResponse); - sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &StateSendDIDAuthenticateResponseEAC2::fireReceivedStartPaosResponse, sStartPaosResponse); - - sTransmit->addTransition(sTransmit, &AbstractState::fireContinue, sSendTransmitResponse); - sTransmit->addTransition(sTransmit, &AbstractState::fireAbort, sSendTransmitResponse); - - sSendTransmitResponse->addTransition(sSendTransmitResponse, &AbstractState::fireContinue, sTransmit); - sSendTransmitResponse->addTransition(sSendTransmitResponse, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - sSendTransmitResponse->addTransition(sSendTransmitResponse, &StateSendTransmitResponse::fireReceivedDisconnect, sSendDisconnectResponse); - sSendTransmitResponse->addTransition(sSendTransmitResponse, &StateSendTransmitResponse::fireReceivedStartPaosResponse, sStartPaosResponse); - - sSendDisconnectResponse->addTransition(sSendDisconnectResponse, &AbstractState::fireContinue, sStartPaosResponse); - sSendDisconnectResponse->addTransition(sSendDisconnectResponse, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - - sStartPaosResponse->addTransition(sStartPaosResponse, &AbstractState::fireContinue, sUpdateRetryCounterFinal); - sStartPaosResponse->addTransition(sStartPaosResponse, &AbstractState::fireAbort, sUpdateRetryCounterFinal); - - sUpdateRetryCounterFinal->addTransition(sUpdateRetryCounterFinal, &AbstractState::fireContinue, sCleanUpReaderManager); - sUpdateRetryCounterFinal->addTransition(sUpdateRetryCounterFinal, &AbstractState::fireAbort, sCleanUpReaderManager); - - sCleanUpReaderManager->addTransition(sCleanUpReaderManager, &AbstractState::fireContinue, sCheckErrorEpilogue); - sCleanUpReaderManager->addTransition(sCleanUpReaderManager, &AbstractState::fireAbort, sCheckErrorEpilogue); + sTrustedChannel->addTransition(sTrustedChannel, &CompositeStateTrustedChannel::fireContinue, sCheckErrorEpilogue); + sTrustedChannel->addTransition(sTrustedChannel, &CompositeStateTrustedChannel::fireAbort, sCheckErrorEpilogue); sCheckErrorEpilogue->addTransition(sCheckErrorEpilogue, &AbstractState::fireContinue, sCheckRefreshAddress); sCheckErrorEpilogue->addTransition(sCheckErrorEpilogue, &AbstractState::fireAbort, sFinal); diff --git a/src/core/controller/SelfAuthController.h b/src/core/controller/SelfAuthController.h index bc4118a..6942938 100644 --- a/src/core/controller/SelfAuthController.h +++ b/src/core/controller/SelfAuthController.h @@ -19,7 +19,7 @@ class SelfAuthController Q_OBJECT public: - SelfAuthController(QSharedPointer pContext); + explicit SelfAuthController(QSharedPointer pContext); virtual ~SelfAuthController() = default; }; diff --git a/src/core/controller/WorkflowController.h b/src/core/controller/WorkflowController.h index b933033..559a75e 100644 --- a/src/core/controller/WorkflowController.h +++ b/src/core/controller/WorkflowController.h @@ -12,8 +12,6 @@ #include #include -class test_ChangePinController; - namespace governikus { @@ -27,7 +25,7 @@ class WorkflowController const QSharedPointer mContext; public: - WorkflowController(const QSharedPointer& pContext); + explicit WorkflowController(const QSharedPointer& pContext); virtual ~WorkflowController(); void run(); diff --git a/src/core/paos/ElementDetector.h b/src/core/paos/ElementDetector.h index b5e50d0..4324707 100644 --- a/src/core/paos/ElementDetector.h +++ b/src/core/paos/ElementDetector.h @@ -27,7 +27,7 @@ class ElementDetector virtual bool handleFoundElement(const QString& pElementName, const QString& pValue, const QXmlStreamAttributes& pAttributes) = 0; public: - ElementDetector(const QByteArray& pXmlData); + explicit ElementDetector(const QByteArray& pXmlData); virtual ~ElementDetector(); }; diff --git a/src/core/paos/PaosHandler.h b/src/core/paos/PaosHandler.h index e756094..32abf73 100644 --- a/src/core/paos/PaosHandler.h +++ b/src/core/paos/PaosHandler.h @@ -30,7 +30,7 @@ class PaosHandler virtual bool handleFoundElement(const QString& pElementName, const QString& pValue, const QXmlStreamAttributes& pAttributes) override; public: - PaosHandler(const QByteArray& pXmlData); + explicit PaosHandler(const QByteArray& pXmlData); PaosType getDetectedPaosType() const; QSharedPointer getPaosMessage() const; diff --git a/src/core/paos/PaosMessage.h b/src/core/paos/PaosMessage.h index 201cb53..98e8e18 100644 --- a/src/core/paos/PaosMessage.h +++ b/src/core/paos/PaosMessage.h @@ -28,7 +28,7 @@ class PaosMessage public: const PaosType mType; - PaosMessage(PaosType pType); + explicit PaosMessage(PaosType pType); virtual ~PaosMessage(); const QString& getMessageId() const diff --git a/src/core/paos/RequestType.h b/src/core/paos/RequestType.h index 3901bb1..65c54f2 100644 --- a/src/core/paos/RequestType.h +++ b/src/core/paos/RequestType.h @@ -16,7 +16,7 @@ class RequestType : public PaosMessage { public: - RequestType(PaosType pType); + explicit RequestType(PaosType pType); virtual ~RequestType(); }; diff --git a/src/core/paos/ResponseType.h b/src/core/paos/ResponseType.h index d395593..6ae37fd 100644 --- a/src/core/paos/ResponseType.h +++ b/src/core/paos/ResponseType.h @@ -22,7 +22,7 @@ class ResponseType ECardApiResult mResult; public: - ResponseType(PaosType pType); + explicit ResponseType(PaosType pType); virtual ~ResponseType(); const ECardApiResult& getResult() const; diff --git a/src/core/paos/element/ConnectionHandleParser.h b/src/core/paos/element/ConnectionHandleParser.h index f8c494f..f0c9955 100644 --- a/src/core/paos/element/ConnectionHandleParser.h +++ b/src/core/paos/element/ConnectionHandleParser.h @@ -21,7 +21,7 @@ class ConnectionHandleParser : public ElementParser { public: - ConnectionHandleParser(QSharedPointer pXmlReader); + explicit ConnectionHandleParser(QSharedPointer pXmlReader); virtual ~ConnectionHandleParser(); ConnectionHandle parse(); diff --git a/src/core/paos/element/Eac1InputType.h b/src/core/paos/element/Eac1InputType.h index 76101b2..5c7c481 100644 --- a/src/core/paos/element/Eac1InputType.h +++ b/src/core/paos/element/Eac1InputType.h @@ -16,12 +16,6 @@ #include #include -class test_StatePrepareChat; -class test_StateExtractCvcsFromEac1InputType; -class test_StatePreVerification; -class test_StateCertificateDescriptionCheck; -class test_StateProcessCertificatesFromEac2; -class test_AuthModel; namespace governikus { @@ -31,11 +25,6 @@ class Eac1InputType { friend class DidAuthenticateEac1Parser; friend class TestAuthContext; - friend class ::test_StateExtractCvcsFromEac1InputType; - friend class ::test_StatePreVerification; - friend class ::test_StateCertificateDescriptionCheck; - friend class ::test_StateProcessCertificatesFromEac2; - friend class ::test_AuthModel; private: QVector > mCvCertificates; diff --git a/src/core/paos/element/ElementParser.cpp b/src/core/paos/element/ElementParser.cpp index fbd79af..8dcc3cf 100644 --- a/src/core/paos/element/ElementParser.cpp +++ b/src/core/paos/element/ElementParser.cpp @@ -58,7 +58,7 @@ bool ElementParser::assertNoDuplicateElement(bool pNotYetSeen) } -void ElementParser::assertMandatoryElement(const QString& pValue, const char* pElementName) +void ElementParser::assertMandatoryElement(const QString& pValue, const char* const pElementName) { if (pValue.isNull()) { diff --git a/src/core/paos/element/ElementParser.h b/src/core/paos/element/ElementParser.h index 63024cd..2aac622 100644 --- a/src/core/paos/element/ElementParser.h +++ b/src/core/paos/element/ElementParser.h @@ -21,7 +21,7 @@ namespace governikus class ElementParser { public: - ElementParser(QSharedPointer pXmlReader); + explicit ElementParser(QSharedPointer pXmlReader); virtual ~ElementParser(); protected: @@ -44,7 +44,7 @@ class ElementParser * \param pValue the elements value to check. * \param pElementName the elements name used to generate the log message. */ - void assertMandatoryElement(const QString& pValue, const char* pElementName); + void assertMandatoryElement(const QString& pValue, const char* const pElementName); /*! * \brief Issues a log warning and sets the error when the list is empty. @@ -52,7 +52,7 @@ class ElementParser * \param pElementName the elements name used to generate the log message. * \return \c true, if the assertion holds, \c false otherwise. */ - template bool assertMandatoryList(const QVector& pList, const char* pElementName); + template bool assertMandatoryList(const QVector& pList, const char* const pElementName); /*! * \brief Issues a log warning and sets the error when a duplicate element has been encountered. @@ -78,7 +78,7 @@ class ElementParser }; -template bool ElementParser::assertMandatoryList(const QVector& pList, const char* pElementName) +template bool ElementParser::assertMandatoryList(const QVector& pList, const char* const pElementName) { if (pList.isEmpty()) { diff --git a/src/core/paos/invoke/DisconnectResponse.h b/src/core/paos/invoke/DisconnectResponse.h index 7d8e63c..e944374 100644 --- a/src/core/paos/invoke/DisconnectResponse.h +++ b/src/core/paos/invoke/DisconnectResponse.h @@ -9,8 +9,6 @@ #include "paos/ResponseType.h" #include "PaosCreator.h" -class test_DisconnectResponse; - namespace governikus { @@ -18,8 +16,6 @@ class DisconnectResponse : public PaosCreator , public ResponseType { - friend class ::test_DisconnectResponse; - private: QString mSlotHandle; diff --git a/src/core/paos/invoke/PaosCreator.h b/src/core/paos/invoke/PaosCreator.h index ef10181..882ad61 100644 --- a/src/core/paos/invoke/PaosCreator.h +++ b/src/core/paos/invoke/PaosCreator.h @@ -10,15 +10,11 @@ #include -class test_PaosCreator; - namespace governikus { class PaosCreator { - friend class ::test_PaosCreator; - public: enum class Namespace { diff --git a/src/core/paos/invoke/StartPaos.h b/src/core/paos/invoke/StartPaos.h index 8370335..819d0ab 100644 --- a/src/core/paos/invoke/StartPaos.h +++ b/src/core/paos/invoke/StartPaos.h @@ -38,7 +38,7 @@ class StartPaos Q_DISABLE_COPY(StartPaos) public: - StartPaos(const QByteArray& pSessionId); + explicit StartPaos(const QByteArray& pSessionId); }; } // namespace governikus diff --git a/src/core/paos/invoke/TransmitResponse.h b/src/core/paos/invoke/TransmitResponse.h index f85fffd..bea2bba 100644 --- a/src/core/paos/invoke/TransmitResponse.h +++ b/src/core/paos/invoke/TransmitResponse.h @@ -9,8 +9,6 @@ #include "paos/ResponseType.h" #include "PaosCreator.h" -class test_TransmitResponse; - namespace governikus { @@ -18,8 +16,6 @@ class TransmitResponse : public PaosCreator , public ResponseType { - friend class ::test_TransmitResponse; - private: QByteArrayList mOutputApdus; diff --git a/src/core/paos/retrieve/DidAuthenticateEac1.h b/src/core/paos/retrieve/DidAuthenticateEac1.h index 3677dfc..465f9c0 100644 --- a/src/core/paos/retrieve/DidAuthenticateEac1.h +++ b/src/core/paos/retrieve/DidAuthenticateEac1.h @@ -25,11 +25,6 @@ class DIDAuthenticateEAC1 { friend class DidAuthenticateEac1Parser; friend class TestAuthContext; - friend class ::test_StatePreVerification; - friend class ::test_StateExtractCvcsFromEac1InputType; - friend class ::test_StateProcessCertificatesFromEac2; - friend class ::test_StateCertificateDescriptionCheck; - friend class ::test_AuthModel; private: ConnectionHandle mConnectionHandle; diff --git a/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.cpp b/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.cpp index d4943b5..bf8cf04 100644 --- a/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.cpp +++ b/src/core/paos/retrieve/DidAuthenticateEacAdditionalParser.cpp @@ -77,7 +77,11 @@ QString DidAuthenticateEacAdditionalParser::parseEacAdditionalInputType() qCDebug(paos) << mXmlReader->name(); if (mXmlReader->name() == QLatin1String("Signature")) { - Q_UNUSED(readUniqueElementText(signature)) + if (!readUniqueElementText(signature)) + { + qCWarning(paos) << "Abort parsing of Signature"; + mParseError = true; + } } else { diff --git a/src/core/paos/retrieve/DidList.h b/src/core/paos/retrieve/DidList.h index 20a45a4..d7d3eac 100644 --- a/src/core/paos/retrieve/DidList.h +++ b/src/core/paos/retrieve/DidList.h @@ -25,7 +25,7 @@ class DIDList bool handleFoundElementConnectionHandle(const QString& pElementName, const QString& pValue); public: - DIDList(const QByteArray& pXmlData); + explicit DIDList(const QByteArray& pXmlData); const ConnectionHandle& getConnectionHandle() const; }; diff --git a/src/core/paos/retrieve/Disconnect.h b/src/core/paos/retrieve/Disconnect.h index 31b7db8..bd1fa92 100644 --- a/src/core/paos/retrieve/Disconnect.h +++ b/src/core/paos/retrieve/Disconnect.h @@ -24,7 +24,7 @@ class Disconnect virtual bool handleFoundElement(const QString& pElementName, const QString& pValue, const QXmlStreamAttributes& pAttributes) override; public: - Disconnect(const QByteArray& pXmlData); + explicit Disconnect(const QByteArray& pXmlData); virtual ~Disconnect() override; const QString& getSlotHandle() const; diff --git a/src/core/paos/retrieve/InitializeFramework.h b/src/core/paos/retrieve/InitializeFramework.h index d6d560b..3707b68 100644 --- a/src/core/paos/retrieve/InitializeFramework.h +++ b/src/core/paos/retrieve/InitializeFramework.h @@ -21,7 +21,7 @@ class InitializeFramework virtual bool handleFoundElement(const QString& pElementName, const QString& pValue, const QXmlStreamAttributes& pAttributes) override; public: - InitializeFramework(const QByteArray& pXmlData); + explicit InitializeFramework(const QByteArray& pXmlData); }; } // namespace governikus diff --git a/src/core/paos/retrieve/PaosParser.h b/src/core/paos/retrieve/PaosParser.h index 981cc43..895300a 100644 --- a/src/core/paos/retrieve/PaosParser.h +++ b/src/core/paos/retrieve/PaosParser.h @@ -16,7 +16,7 @@ class PaosParser : public ElementParser { public: - PaosParser(const QString& pMessageName); + explicit PaosParser(const QString& pMessageName); virtual ~PaosParser(); PaosMessage* parse(const QByteArray& pXmlData); diff --git a/src/core/paos/retrieve/StartPaosResponse.h b/src/core/paos/retrieve/StartPaosResponse.h index 998b3f4..db9785d 100644 --- a/src/core/paos/retrieve/StartPaosResponse.h +++ b/src/core/paos/retrieve/StartPaosResponse.h @@ -23,7 +23,7 @@ class StartPaosResponse QString mResultMajor, mResultMinor, mResultMessage; public: - StartPaosResponse(const QByteArray& pXmlData); + explicit StartPaosResponse(const QByteArray& pXmlData); private: void parse(); diff --git a/src/core/paos/retrieve/Transmit.h b/src/core/paos/retrieve/Transmit.h index 672c0b2..dff61d3 100644 --- a/src/core/paos/retrieve/Transmit.h +++ b/src/core/paos/retrieve/Transmit.h @@ -22,7 +22,7 @@ class Transmit public: Transmit(); - Transmit(const QByteArray& pXmlData); + explicit Transmit(const QByteArray& pXmlData); virtual ~Transmit(); const QString& getSlotHandle() const diff --git a/src/core/states/AbstractState.cpp b/src/core/states/AbstractState.cpp index 5240c8f..169fa84 100644 --- a/src/core/states/AbstractState.cpp +++ b/src/core/states/AbstractState.cpp @@ -42,7 +42,7 @@ void AbstractState::setStateName(const QString& pName) } -QString AbstractState::getClassName(const char* pName) +QString AbstractState::getClassName(const char* const pName) { QString className = QString::fromLatin1(pName); if (className.contains(QLatin1Char(':'))) @@ -65,7 +65,7 @@ void AbstractState::onStateApprovedChanged() void AbstractState::onEntry(QEvent* pEvent) { - Q_UNUSED(pEvent); + Q_UNUSED(pEvent) if (mConnectOnCardRemoved) { const auto readerManager = Env::getSingleton(); diff --git a/src/core/states/AbstractState.h b/src/core/states/AbstractState.h index e77bc34..784864d 100644 --- a/src/core/states/AbstractState.h +++ b/src/core/states/AbstractState.h @@ -27,12 +27,13 @@ class AbstractState const QSharedPointer mContext; const bool mConnectOnCardRemoved; - explicit AbstractState(const QSharedPointer& pContext, bool pConnectOnCardRemoved); virtual void run() = 0; protected: QVector mConnections; + explicit AbstractState(const QSharedPointer& pContext, bool pConnectOnCardRemoved = true); + void onExit(QEvent* pEvent) override; void clearConnections(); @@ -41,7 +42,7 @@ class AbstractState void updateStartPaosResult(const ECardApiResult& pStartPaosResult); public: - static QString getClassName(const char* pName); + static QString getClassName(const char* const pName); template static bool isState(const QString& pState) diff --git a/src/core/states/CompositeStatePace.cpp b/src/core/states/CompositeStatePace.cpp index 3025266..e0c0ae1 100644 --- a/src/core/states/CompositeStatePace.cpp +++ b/src/core/states/CompositeStatePace.cpp @@ -5,14 +5,15 @@ #include "CompositeStatePace.h" #include "context/WorkflowContext.h" -#include "states/CompositeStateSelectCard.h" #include "states/StateBuilder.h" #include "states/StateClearPacePasswords.h" +#include "states/StateConnectCard.h" #include "states/StateDestroyPace.h" #include "states/StateEnterPacePassword.h" #include "states/StateEstablishPaceChannel.h" #include "states/StateMaintainCardConnection.h" #include "states/StatePreparePace.h" +#include "states/StateSelectReader.h" #include "states/StateUnfortunateCardPosition.h" #include "states/StateUpdateRetryCounter.h" #include "states/StateVerifyRetryCounter.h" @@ -26,7 +27,8 @@ CompositeStatePace::CompositeStatePace(const QSharedPointer& pC { auto sMaintainCardConnection = StateBuilder::createState(mContext); auto sClearPacePasswordsOnError = StateBuilder::createState(mContext); - auto sSelectCard = new CompositeStateSelectCard(pContext); + auto sSelectReader = StateBuilder::createState(mContext); + auto sConnectCard = StateBuilder::createState(mContext); auto sUpdateRetryCounter = StateBuilder::createState(mContext); auto sVerifyRetryCounter = StateBuilder::createState(mContext); auto sPreparePace = StateBuilder::createState(mContext); @@ -38,7 +40,8 @@ CompositeStatePace::CompositeStatePace(const QSharedPointer& pC sMaintainCardConnection->setParent(this); sClearPacePasswordsOnError->setParent(this); - sSelectCard->setParent(this); + sSelectReader->setParent(this); + sConnectCard->setParent(this); sUpdateRetryCounter->setParent(this); sVerifyRetryCounter->setParent(this); sPreparePace->setParent(this); @@ -51,15 +54,20 @@ CompositeStatePace::CompositeStatePace(const QSharedPointer& pC setInitialState(sMaintainCardConnection); sMaintainCardConnection->addTransition(sMaintainCardConnection, &StateMaintainCardConnection::fireContinue, sVerifyRetryCounter); - sMaintainCardConnection->addTransition(sMaintainCardConnection, &StateMaintainCardConnection::fireNoCardConnection, sSelectCard); + sMaintainCardConnection->addTransition(sMaintainCardConnection, &StateMaintainCardConnection::fireNoCardConnection, sSelectReader); sMaintainCardConnection->addTransition(sMaintainCardConnection, &StateMaintainCardConnection::fireForceUpdateRetryCounter, sUpdateRetryCounter); sMaintainCardConnection->addTransition(sMaintainCardConnection, &StateMaintainCardConnection::fireAbort, sClearPacePasswordsOnError); connect(sClearPacePasswordsOnError, &AbstractState::fireContinue, this, &CompositeStatePace::fireAbort); connect(sClearPacePasswordsOnError, &AbstractState::fireAbort, this, &CompositeStatePace::fireAbort); - sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireContinue, sUpdateRetryCounter); - sSelectCard->addTransition(sSelectCard, &CompositeStateSelectCard::fireAbort, sMaintainCardConnection); + sSelectReader->addTransition(sSelectReader, &StateSelectReader::fireRetry, sSelectReader); + sSelectReader->addTransition(sSelectReader, &AbstractState::fireContinue, sConnectCard); + sSelectReader->addTransition(sSelectReader, &AbstractState::fireAbort, sMaintainCardConnection); + + sConnectCard->addTransition(sConnectCard, &StateConnectCard::fireRetry, sSelectReader); + sConnectCard->addTransition(sConnectCard, &AbstractState::fireContinue, sUpdateRetryCounter); + sConnectCard->addTransition(sConnectCard, &AbstractState::fireAbort, sMaintainCardConnection); sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireContinue, sVerifyRetryCounter); sUpdateRetryCounter->addTransition(sUpdateRetryCounter, &AbstractState::fireAbort, sMaintainCardConnection); diff --git a/src/core/states/CompositeStateSelectCard.cpp b/src/core/states/CompositeStateSelectCard.cpp deleted file mode 100644 index 4e56728..0000000 --- a/src/core/states/CompositeStateSelectCard.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "CompositeStateSelectCard.h" - -#include "context/WorkflowContext.h" -#include "states/StateBuilder.h" -#include "states/StateConnectCard.h" -#include "states/StateSelectReader.h" - -using namespace governikus; - - -CompositeStateSelectCard::CompositeStateSelectCard(const QSharedPointer& pContext) - : QState() - , mContext(pContext) -{ - auto sSelectReader = StateBuilder::createState(mContext); - auto sConnectCard = StateBuilder::createState(mContext); - - sSelectReader->setParent(this); - sConnectCard->setParent(this); - - setInitialState(sSelectReader); - - sSelectReader->addTransition(sSelectReader, &StateSelectReader::fireRetry, sSelectReader); - sSelectReader->addTransition(sSelectReader, &AbstractState::fireContinue, sConnectCard); - connect(sSelectReader, &AbstractState::fireAbort, this, &CompositeStateSelectCard::fireAbort); - - sConnectCard->addTransition(sConnectCard, &StateConnectCard::fireRetry, sSelectReader); - sConnectCard->addTransition(sConnectCard, &StateConnectCard::fireReaderRemoved, sSelectReader); - connect(sConnectCard, &AbstractState::fireContinue, this, &CompositeStateSelectCard::fireContinue); - connect(sConnectCard, &AbstractState::fireAbort, this, &CompositeStateSelectCard::fireAbort); -} - - -CompositeStateSelectCard::~CompositeStateSelectCard() -{ -} diff --git a/src/core/states/CompositeStateTrustedChannel.cpp b/src/core/states/CompositeStateTrustedChannel.cpp new file mode 100644 index 0000000..c5d57fb --- /dev/null +++ b/src/core/states/CompositeStateTrustedChannel.cpp @@ -0,0 +1,167 @@ +/*! + * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "CompositeStateTrustedChannel.h" + +#include "context/WorkflowContext.h" +#include "states/CompositeStatePace.h" +#include "states/CompositeStateProcessCvcsAndSetRights.h" +#include "states/StateBuilder.h" +#include "states/StateCleanUpReaderManager.h" +#include "states/StateClearPacePasswords.h" +#include "states/StateDidAuthenticateEac1.h" +#include "states/StateDidAuthenticateEac2.h" +#include "states/StateDidList.h" +#include "states/StateEACAdditionalInputType.h" +#include "states/StateGenericSendReceive.h" +#include "states/StateGetTcToken.h" +#include "states/StateInitializeFramework.h" +#include "states/StateProcessCertificatesFromEac2.h" +#include "states/StateStartPaos.h" +#include "states/StateStartPaosResponse.h" +#include "states/StateTransmit.h" +#include "states/StateUpdateRetryCounter.h" + +using namespace governikus; + + +CompositeStateTrustedChannel::CompositeStateTrustedChannel(const QSharedPointer& pContext) + : QState() + , mContext(pContext) +{ + auto sGetTcToken = StateBuilder::createState(mContext); + auto sStartPaos = StateBuilder::createState(mContext); + auto sSendStartPaos = StateBuilder::createState(mContext); + auto sInitializeFramework = StateBuilder::createState(mContext); + auto sSendInitializeFrameworkResponse = StateBuilder::createState(mContext); + auto sDidList = StateBuilder::createState(mContext); + auto sSendDidListResponse = StateBuilder::createState(mContext); + auto sProcessCvcsAndSetRights = new CompositeStateProcessCvcsAndSetRights(mContext); + auto sStatePace = new CompositeStatePace(mContext); + auto sClearPacePasswords = StateBuilder::createState(mContext); + auto sDidAuthenticateEac1 = StateBuilder::createState(mContext); + auto sSendDidAuthenticateResponseEac1 = StateBuilder::createState(mContext); + auto sEacAdditionalInputType = StateBuilder::createState(mContext); + auto sSendDidAuthenticatResponseEacAdditionalInput = StateBuilder::createState(mContext); + auto sProcessCertificatesFromEac2 = StateBuilder::createState(mContext); + auto sDidAuthenticateEac2 = StateBuilder::createState(mContext); + auto sSendDidAuthenticateResponseEac2 = StateBuilder::createState(mContext); + auto sTransmit = StateBuilder::createState(mContext); + auto sSendTransmitResponse = StateBuilder::createState(mContext); + auto sSendDisconnectResponse = StateBuilder::createState(mContext); + auto sStartPaosResponse = StateBuilder::createState(mContext); + auto sUpdateRetryCounterFinal = StateBuilder::createState(mContext); + auto sCleanUpReaderManager = StateBuilder::createState(mContext); + + sGetTcToken->setParent(this); + sStartPaos->setParent(this); + sSendStartPaos->setParent(this); + sInitializeFramework->setParent(this); + sSendInitializeFrameworkResponse->setParent(this); + sDidList->setParent(this); + sSendDidListResponse->setParent(this); + sProcessCvcsAndSetRights->setParent(this); + sStatePace->setParent(this); + sClearPacePasswords->setParent(this); + sDidAuthenticateEac1->setParent(this); + sSendDidAuthenticateResponseEac1->setParent(this); + sEacAdditionalInputType->setParent(this); + sSendDidAuthenticatResponseEacAdditionalInput->setParent(this); + sProcessCertificatesFromEac2->setParent(this); + sDidAuthenticateEac2->setParent(this); + sSendDidAuthenticateResponseEac2->setParent(this); + sTransmit->setParent(this); + sSendTransmitResponse->setParent(this); + sSendDisconnectResponse->setParent(this); + sStartPaosResponse->setParent(this); + sUpdateRetryCounterFinal->setParent(this); + sCleanUpReaderManager->setParent(this); + + setInitialState(sGetTcToken); + + sGetTcToken->addTransition(sGetTcToken, &AbstractState::fireContinue, sStartPaos); + sGetTcToken->addTransition(sGetTcToken, &AbstractState::fireAbort, sCleanUpReaderManager); + + sStartPaos->addTransition(sStartPaos, &AbstractState::fireContinue, sSendStartPaos); + sStartPaos->addTransition(sStartPaos, &AbstractState::fireAbort, sCleanUpReaderManager); + + sSendStartPaos->addTransition(sSendStartPaos, &AbstractState::fireContinue, sInitializeFramework); + sSendStartPaos->addTransition(sSendStartPaos, &AbstractState::fireAbort, sCleanUpReaderManager); + sSendStartPaos->addTransition(sSendStartPaos, &StateSendStartPaos::fireReceivedDidList, sDidList); + sSendStartPaos->addTransition(sSendStartPaos, &StateSendStartPaos::fireReceivedExtractCvcsFromEac1InputType, sProcessCvcsAndSetRights); + sSendStartPaos->addTransition(sSendStartPaos, &StateSendStartPaos::fireReceivedStartPaosResponse, sStartPaosResponse); + + sInitializeFramework->addTransition(sInitializeFramework, &AbstractState::fireContinue, sSendInitializeFrameworkResponse); + sInitializeFramework->addTransition(sInitializeFramework, &AbstractState::fireAbort, sSendInitializeFrameworkResponse); + + sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &AbstractState::fireContinue, sDidList); + sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &AbstractState::fireAbort, sCleanUpReaderManager); + sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &StateSendInitializeFrameworkResponse::fireReceivedExtractCvcsFromEac1InputType, sProcessCvcsAndSetRights); + sSendInitializeFrameworkResponse->addTransition(sSendInitializeFrameworkResponse, &StateSendInitializeFrameworkResponse::fireReceivedStartPaosResponse, sStartPaosResponse); + + sDidList->addTransition(sDidList, &AbstractState::fireContinue, sSendDidListResponse); + sDidList->addTransition(sDidList, &AbstractState::fireAbort, sSendDidListResponse); + + sSendDidListResponse->addTransition(sSendDidListResponse, &AbstractState::fireContinue, sProcessCvcsAndSetRights); + sSendDidListResponse->addTransition(sSendDidListResponse, &AbstractState::fireAbort, sCleanUpReaderManager); + sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedDisconnect, sSendDisconnectResponse); + sSendDidListResponse->addTransition(sSendDidListResponse, &StateSendDIDListResponse::fireReceivedStartPaosResponse, sStartPaosResponse); + + sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireContinue, sStatePace); + sProcessCvcsAndSetRights->addTransition(sProcessCvcsAndSetRights, &CompositeStateProcessCvcsAndSetRights::fireAbort, sSendDidAuthenticateResponseEac1); + + sStatePace->addTransition(sStatePace, &CompositeStatePace::firePaceChannelEstablished, sClearPacePasswords); + sStatePace->addTransition(sStatePace, &CompositeStatePace::firePacePukEstablished, sStatePace); + sStatePace->addTransition(sStatePace, &CompositeStatePace::fireAbort, sSendDidAuthenticateResponseEac1); + + sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireContinue, sDidAuthenticateEac1); + sClearPacePasswords->addTransition(sClearPacePasswords, &AbstractState::fireAbort, sDidAuthenticateEac1); + + sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireContinue, sSendDidAuthenticateResponseEac1); + sDidAuthenticateEac1->addTransition(sDidAuthenticateEac1, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac1); + + sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &AbstractState::fireContinue, sEacAdditionalInputType); + sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &AbstractState::fireAbort, sUpdateRetryCounterFinal); + sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &StateSendDIDAuthenticateResponseEAC1::fireReceivedDisconnect, sSendDisconnectResponse); + sSendDidAuthenticateResponseEac1->addTransition(sSendDidAuthenticateResponseEac1, &StateSendDIDAuthenticateResponseEAC1::fireReceivedStartPaosResponse, sStartPaosResponse); + + sEacAdditionalInputType->addTransition(sEacAdditionalInputType, &AbstractState::fireContinue, sProcessCertificatesFromEac2); + sEacAdditionalInputType->addTransition(sEacAdditionalInputType, &AbstractState::fireAbort, sSendDidAuthenticatResponseEacAdditionalInput); + sEacAdditionalInputType->addTransition(sEacAdditionalInputType, &StateEACAdditionalInputType::fireSendDidAuthenticatResponse, sSendDidAuthenticatResponseEacAdditionalInput); + + sSendDidAuthenticatResponseEacAdditionalInput->addTransition(sSendDidAuthenticatResponseEacAdditionalInput, &AbstractState::fireContinue, sProcessCertificatesFromEac2); + sSendDidAuthenticatResponseEacAdditionalInput->addTransition(sSendDidAuthenticatResponseEacAdditionalInput, &AbstractState::fireAbort, sUpdateRetryCounterFinal); + sSendDidAuthenticatResponseEacAdditionalInput->addTransition(sSendDidAuthenticatResponseEacAdditionalInput, &StateSendDIDAuthenticateResponseEACAdditionalInputType::fireReceivedStartPaosResponse, sStartPaosResponse); + + sProcessCertificatesFromEac2->addTransition(sProcessCertificatesFromEac2, &AbstractState::fireContinue, sDidAuthenticateEac2); + sProcessCertificatesFromEac2->addTransition(sProcessCertificatesFromEac2, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac2); + + sDidAuthenticateEac2->addTransition(sDidAuthenticateEac2, &AbstractState::fireContinue, sSendDidAuthenticateResponseEac2); + sDidAuthenticateEac2->addTransition(sDidAuthenticateEac2, &AbstractState::fireAbort, sSendDidAuthenticateResponseEac2); + + sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &AbstractState::fireContinue, sTransmit); + sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &AbstractState::fireAbort, sUpdateRetryCounterFinal); + sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &StateSendDIDAuthenticateResponseEAC2::fireReceivedDisconnect, sSendDisconnectResponse); + sSendDidAuthenticateResponseEac2->addTransition(sSendDidAuthenticateResponseEac2, &StateSendDIDAuthenticateResponseEAC2::fireReceivedStartPaosResponse, sStartPaosResponse); + + sTransmit->addTransition(sTransmit, &AbstractState::fireContinue, sSendTransmitResponse); + sTransmit->addTransition(sTransmit, &AbstractState::fireAbort, sSendTransmitResponse); + + sSendTransmitResponse->addTransition(sSendTransmitResponse, &AbstractState::fireContinue, sTransmit); + sSendTransmitResponse->addTransition(sSendTransmitResponse, &AbstractState::fireAbort, sUpdateRetryCounterFinal); + sSendTransmitResponse->addTransition(sSendTransmitResponse, &StateSendTransmitResponse::fireReceivedDisconnect, sSendDisconnectResponse); + sSendTransmitResponse->addTransition(sSendTransmitResponse, &StateSendTransmitResponse::fireReceivedStartPaosResponse, sStartPaosResponse); + + sSendDisconnectResponse->addTransition(sSendDisconnectResponse, &AbstractState::fireContinue, sStartPaosResponse); + sSendDisconnectResponse->addTransition(sSendDisconnectResponse, &AbstractState::fireAbort, sUpdateRetryCounterFinal); + + sStartPaosResponse->addTransition(sStartPaosResponse, &AbstractState::fireContinue, sUpdateRetryCounterFinal); + sStartPaosResponse->addTransition(sStartPaosResponse, &AbstractState::fireAbort, sUpdateRetryCounterFinal); + + sUpdateRetryCounterFinal->addTransition(sUpdateRetryCounterFinal, &AbstractState::fireContinue, sCleanUpReaderManager); + sUpdateRetryCounterFinal->addTransition(sUpdateRetryCounterFinal, &AbstractState::fireAbort, sCleanUpReaderManager); + + connect(sCleanUpReaderManager, &AbstractState::fireContinue, this, &CompositeStateTrustedChannel::fireContinue); + connect(sCleanUpReaderManager, &AbstractState::fireAbort, this, &CompositeStateTrustedChannel::fireAbort); +} diff --git a/src/core/states/CompositeStateSelectCard.h b/src/core/states/CompositeStateTrustedChannel.h similarity index 51% rename from src/core/states/CompositeStateSelectCard.h rename to src/core/states/CompositeStateTrustedChannel.h index 21b02e2..78cde15 100644 --- a/src/core/states/CompositeStateSelectCard.h +++ b/src/core/states/CompositeStateTrustedChannel.h @@ -1,7 +1,5 @@ /*! - * \brief Composite state for selecting a card. - * - * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany */ #pragma once @@ -15,7 +13,7 @@ namespace governikus class WorkflowContext; -class CompositeStateSelectCard +class CompositeStateTrustedChannel : public QState { Q_OBJECT @@ -23,8 +21,8 @@ class CompositeStateSelectCard const QSharedPointer mContext; public: - explicit CompositeStateSelectCard(const QSharedPointer& pContext); - virtual ~CompositeStateSelectCard(); + explicit CompositeStateTrustedChannel(const QSharedPointer& pContext); + virtual ~CompositeStateTrustedChannel() = default; Q_SIGNALS: void fireContinue(); diff --git a/src/core/states/FinalState.cpp b/src/core/states/FinalState.cpp index 47ef352..a46475d 100644 --- a/src/core/states/FinalState.cpp +++ b/src/core/states/FinalState.cpp @@ -15,7 +15,7 @@ void FinalState::run() // We need a separate QFinaleState since we do not want the controller to stop working until // this state has been approved. We add the QFinalState at this point since we need to know // the state machine and since we do not want to alter our ctor pattern. - QFinalState* sStopMachine = new QFinalState(); + auto* sStopMachine = new QFinalState(); machine()->addState(sStopMachine); addTransition(this, &AbstractState::fireContinue, sStopMachine); addTransition(this, &AbstractState::fireAbort, sStopMachine); diff --git a/src/core/states/FinalState.h b/src/core/states/FinalState.h index 77c0dce..57c7546 100644 --- a/src/core/states/FinalState.h +++ b/src/core/states/FinalState.h @@ -6,14 +6,15 @@ #pragma once -#include "states/AbstractGenericState.h" - +#include "AbstractState.h" +#include "GenericContextContainer.h" namespace governikus { class FinalState - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT @@ -23,14 +24,15 @@ class FinalState protected: void onEntry(QEvent* pEvent) override { - AbstractGenericState::onEntry(pEvent); + AbstractState::onEntry(pEvent); getContext()->setWorkflowFinished(true); } public: explicit FinalState(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/AbstractGenericState.h b/src/core/states/GenericContextContainer.h similarity index 57% rename from src/core/states/AbstractGenericState.h rename to src/core/states/GenericContextContainer.h index e57ad52..ac5e2bf 100644 --- a/src/core/states/AbstractGenericState.h +++ b/src/core/states/GenericContextContainer.h @@ -9,34 +9,31 @@ #pragma once -#include "AbstractState.h" - -#include +#include "context/WorkflowContext.h" namespace governikus { template -class AbstractGenericState - : public AbstractState +class GenericContextContainer { + private: + const QSharedPointer mTypedContext; + public: - explicit AbstractGenericState(const QSharedPointer& pContext, bool pConnectOnCardRemoved = true) - : AbstractState(pContext, pConnectOnCardRemoved) + explicit GenericContextContainer(const QSharedPointer& pContext) + : mTypedContext(pContext.staticCast()) { - Q_ASSERT(mContext.objectCast()); + Q_ASSERT(pContext.objectCast()); } - virtual ~AbstractGenericState() - { - } - + virtual ~GenericContextContainer() = default; virtual QSharedPointer getContext() { - return mContext.staticCast(); + return mTypedContext; } diff --git a/src/core/states/StateActivateStoreFeedbackDialog.cpp b/src/core/states/StateActivateStoreFeedbackDialog.cpp index 486d3b4..78d6e86 100644 --- a/src/core/states/StateActivateStoreFeedbackDialog.cpp +++ b/src/core/states/StateActivateStoreFeedbackDialog.cpp @@ -11,14 +11,15 @@ using namespace governikus; StateActivateStoreFeedbackDialog::StateActivateStoreFeedbackDialog(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } void StateActivateStoreFeedbackDialog::run() { -#if defined(Q_OS_ANDROID) +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) auto& settings = Env::getSingleton()->getGeneralSettings(); if (getContext()->getStatus().isNoError() && settings.askForStoreFeedback()) { diff --git a/src/core/states/StateActivateStoreFeedbackDialog.h b/src/core/states/StateActivateStoreFeedbackDialog.h index cc4f807..aefc117 100644 --- a/src/core/states/StateActivateStoreFeedbackDialog.h +++ b/src/core/states/StateActivateStoreFeedbackDialog.h @@ -7,15 +7,17 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateActivateStoreFeedbackDialog - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateCertificateDescriptionCheck.cpp b/src/core/states/StateCertificateDescriptionCheck.cpp index 4c59d82..1f185e2 100644 --- a/src/core/states/StateCertificateDescriptionCheck.cpp +++ b/src/core/states/StateCertificateDescriptionCheck.cpp @@ -16,7 +16,8 @@ Q_DECLARE_LOGGING_CATEGORY(developermode) StateCertificateDescriptionCheck::StateCertificateDescriptionCheck(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateCertificateDescriptionCheck.h b/src/core/states/StateCertificateDescriptionCheck.h index d71dafd..9bb39b5 100644 --- a/src/core/states/StateCertificateDescriptionCheck.h +++ b/src/core/states/StateCertificateDescriptionCheck.h @@ -4,19 +4,20 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateCertificateDescriptionCheck - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; - friend class ::test_StateCertificateDescriptionCheck; explicit StateCertificateDescriptionCheck(const QSharedPointer& pContext); virtual void run() override; diff --git a/src/core/states/StateChangePin.cpp b/src/core/states/StateChangePin.cpp index 2b56388..eb30b9b 100644 --- a/src/core/states/StateChangePin.cpp +++ b/src/core/states/StateChangePin.cpp @@ -10,7 +10,8 @@ using namespace governikus; StateChangePin::StateChangePin(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -32,6 +33,7 @@ void StateChangePin::onSetEidPinDone(QSharedPointer pCommand) switch (returnCode) { case CardReturnCode::OK: + //: INFO ALL_PLATFORMS The pin was changed successfully. getContext()->setSuccessMessage(tr("You have successfully changed your PIN.")); Q_EMIT fireContinue(); break; diff --git a/src/core/states/StateChangePin.h b/src/core/states/StateChangePin.h index eb7f219..6defed4 100644 --- a/src/core/states/StateChangePin.h +++ b/src/core/states/StateChangePin.h @@ -6,8 +6,9 @@ #pragma once -#include "AbstractGenericState.h" +#include "AbstractState.h" #include "context/ChangePinContext.h" +#include "GenericContextContainer.h" class test_StateChangePin; @@ -15,7 +16,8 @@ namespace governikus { class StateChangePin - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateCheckCertificates.cpp b/src/core/states/StateCheckCertificates.cpp index 0819ccb..5b093b3 100644 --- a/src/core/states/StateCheckCertificates.cpp +++ b/src/core/states/StateCheckCertificates.cpp @@ -13,7 +13,8 @@ Q_DECLARE_LOGGING_CATEGORY(developermode) StateCheckCertificates::StateCheckCertificates(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateCheckCertificates.h b/src/core/states/StateCheckCertificates.h index f221299..66a4da9 100644 --- a/src/core/states/StateCheckCertificates.h +++ b/src/core/states/StateCheckCertificates.h @@ -8,14 +8,16 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateCheckCertificates - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateCheckError.cpp b/src/core/states/StateCheckError.cpp index 69a76ee..94c8ab7 100644 --- a/src/core/states/StateCheckError.cpp +++ b/src/core/states/StateCheckError.cpp @@ -7,7 +7,8 @@ using namespace governikus; StateCheckError::StateCheckError(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateCheckError.h b/src/core/states/StateCheckError.h index 2765763..f91777d 100644 --- a/src/core/states/StateCheckError.h +++ b/src/core/states/StateCheckError.h @@ -7,14 +7,16 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateCheckError - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateCheckRefreshAddress.cpp b/src/core/states/StateCheckRefreshAddress.cpp index 0b6a87b..df5eb69 100644 --- a/src/core/states/StateCheckRefreshAddress.cpp +++ b/src/core/states/StateCheckRefreshAddress.cpp @@ -27,7 +27,8 @@ Q_DECLARE_LOGGING_CATEGORY(network) StateCheckRefreshAddress::StateCheckRefreshAddress(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) , mReply(nullptr) , mUrl() , mSubjectUrl() @@ -101,7 +102,7 @@ void StateCheckRefreshAddress::run() auto refreshAddrError = QStringLiteral("Invalid RefreshAddress: %1").arg(mUrl.toString()); if (!mUrl.isValid()) { - qDebug() << refreshAddrError; + qDebug().noquote() << refreshAddrError; Q_EMIT fireContinue(); return; } @@ -113,7 +114,7 @@ void StateCheckRefreshAddress::run() } else { - qDebug() << refreshAddrError; + qDebug().noquote() << refreshAddrError; Q_EMIT fireContinue(); return; } @@ -234,7 +235,7 @@ bool StateCheckRefreshAddress::checkSslConnectionAndSaveCertificate(const QSslCo return false; case CertificateChecker::CertificateStatus::Hash_Not_In_Description: - reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description, issuerName)); + reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Network_Ssl_Hash_Not_In_Certificate_Description, issuerName)); return false; } @@ -269,7 +270,7 @@ void StateCheckRefreshAddress::onNetworkReply() reportCommunicationError(GlobalStatus(GlobalStatus::Code::Network_Proxy_Error)); break; - case NetworkManager::NetworkError::SslError: + case NetworkManager::NetworkError::SecurityError: reportCommunicationError(GlobalStatus(GlobalStatus::Code::Network_Ssl_Establishment_Error)); break; @@ -408,3 +409,11 @@ void StateCheckRefreshAddress::onNetworkErrorFetchingServerCertificate(QNetworkR qCritical() << "An error occured fetching the server certificate:" << mReply->errorString(); reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Network_Empty_Redirect_Url)); } + + +void StateCheckRefreshAddress::onEntry(QEvent* pEvent) +{ + //: INFO ALL_PLATFORMS Status message after the communication between card and server is completed, the result is being forwarded to the provider. + getContext()->setProgress(80, tr("Sending data to service provider")); + AbstractState::onEntry(pEvent); +} diff --git a/src/core/states/StateCheckRefreshAddress.h b/src/core/states/StateCheckRefreshAddress.h index 462c31d..7558537 100644 --- a/src/core/states/StateCheckRefreshAddress.h +++ b/src/core/states/StateCheckRefreshAddress.h @@ -7,8 +7,9 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" #include #include @@ -22,7 +23,8 @@ namespace governikus { class StateCheckRefreshAddress - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; @@ -57,6 +59,7 @@ class StateCheckRefreshAddress public: virtual ~StateCheckRefreshAddress() override; + void onEntry(QEvent* pEvent) override; }; } // namespace governikus diff --git a/src/core/states/StateCleanUpReaderManager.cpp b/src/core/states/StateCleanUpReaderManager.cpp index fe0e465..f7a6ad9 100644 --- a/src/core/states/StateCleanUpReaderManager.cpp +++ b/src/core/states/StateCleanUpReaderManager.cpp @@ -13,7 +13,8 @@ using namespace governikus; StateCleanUpReaderManager::StateCleanUpReaderManager(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateCleanUpReaderManager.h b/src/core/states/StateCleanUpReaderManager.h index 357b4a1..10b773a 100644 --- a/src/core/states/StateCleanUpReaderManager.h +++ b/src/core/states/StateCleanUpReaderManager.h @@ -7,13 +7,15 @@ #pragma once -#include "AbstractGenericState.h" +#include "AbstractState.h" +#include "GenericContextContainer.h" namespace governikus { class StateCleanUpReaderManager - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateClearPacePasswords.cpp b/src/core/states/StateClearPacePasswords.cpp index bba9bcc..c22437b 100644 --- a/src/core/states/StateClearPacePasswords.cpp +++ b/src/core/states/StateClearPacePasswords.cpp @@ -9,7 +9,8 @@ using namespace governikus; StateClearPacePasswords::StateClearPacePasswords(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateClearPacePasswords.h b/src/core/states/StateClearPacePasswords.h index 641006f..01b98ab 100644 --- a/src/core/states/StateClearPacePasswords.h +++ b/src/core/states/StateClearPacePasswords.h @@ -4,14 +4,17 @@ #pragma once +#include "AbstractState.h" #include "context/WorkflowContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" + namespace governikus { class StateClearPacePasswords - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateConnectCard.cpp b/src/core/states/StateConnectCard.cpp index e9098cd..ecfa53a 100644 --- a/src/core/states/StateConnectCard.cpp +++ b/src/core/states/StateConnectCard.cpp @@ -5,6 +5,9 @@ #include "CardConnection.h" #include "ReaderManager.h" #include "StateConnectCard.h" +#if defined(Q_OS_ANDROID) +#include "SurveyModel.h" +#endif #include @@ -13,7 +16,8 @@ Q_DECLARE_LOGGING_CATEGORY(statemachine) using namespace governikus; StateConnectCard::StateConnectCard(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -23,8 +27,6 @@ void StateConnectCard::run() const auto readerManager = Env::getSingleton(); mConnections += connect(readerManager, &ReaderManager::fireCardInserted, this, &StateConnectCard::onCardInserted); mConnections += connect(readerManager, &ReaderManager::fireReaderRemoved, this, &StateConnectCard::onReaderRemoved); - mConnections += connect(getContext().data(), &WorkflowContext::fireAbortCardSelection, this, &StateConnectCard::onAbort); - mConnections += connect(getContext().data(), &WorkflowContext::fireReaderPlugInTypesChanged, this, &StateConnectCard::fireRetry); onCardInserted(); } @@ -35,11 +37,11 @@ void StateConnectCard::onCardInserted() ReaderInfo readerInfo = readerManager->getReaderInfo(getContext()->getReaderName()); if (readerInfo.hasEidCard()) { - if (readerInfo.sufficientApduLength() && (!readerInfo.isPinDeactivated() || getContext()->isCanAllowedMode())) - { - qCDebug(statemachine) << "Card has been inserted, trying to connect"; - mConnections += readerManager->callCreateCardConnectionCommand(readerInfo.getName(), this, &StateConnectCard::onCommandDone); - } +#if defined(Q_OS_ANDROID) + Env::getSingleton()->setMaximumNfcPacketLength(readerInfo.getMaxApduLength()); +#endif + qCDebug(statemachine) << "Card has been inserted, trying to connect"; + mConnections += readerManager->callCreateCardConnectionCommand(readerInfo.getName(), this, &StateConnectCard::onCommandDone); } } @@ -56,7 +58,17 @@ void StateConnectCard::onCommandDone(QSharedPointer qCDebug(statemachine) << "Card connection was successful"; getContext()->setCardConnection(pCommand->getCardConnection()); - Q_EMIT fireContinue(); + + const auto readerManager = Env::getSingleton(); + ReaderInfo readerInfo = readerManager->getReaderInfo(getContext()->getReaderName()); + if (readerInfo.sufficientApduLength() && (!readerInfo.isPinDeactivated() || getContext()->isCanAllowedMode())) + { + Q_EMIT fireContinue(); + } + else if (readerInfo.isPinDeactivated() && !getContext()->isCanAllowedMode()) + { + getContext()->getCardConnection()->setProgressMessage(tr("The online identification function is disabled.")); + } } @@ -64,21 +76,21 @@ void StateConnectCard::onReaderRemoved(const QString& pReaderName) { if (pReaderName == getContext()->getReaderName()) { - Q_EMIT fireReaderRemoved(); + Q_EMIT fireRetry(); } } -void StateConnectCard::onAbort() +void StateConnectCard::onEntry(QEvent* pEvent) { - const QSharedPointer context = getContext(); + const WorkflowContext* const context = getContext().data(); Q_ASSERT(context); - const auto readerManager = Env::getSingleton(); - ReaderInfo readerInfo = readerManager->getReaderInfo(context->getReaderName()); - if (readerInfo.isConnected()) - { - readerManager->disconnectReader(readerInfo.getName()); - } - Q_EMIT fireRetry(); + /* + * Note: the plugin types to be used in this state must be already set in the workflow context before this state is entered. + * Changing the plugin types in the context, e.g. from {NFC} to {BLUETOOTH}, causes the state to be left with a fireRetry signal. + */ + mConnections += connect(context, &WorkflowContext::fireReaderPlugInTypesChanged, this, &StateConnectCard::fireRetry); + + AbstractState::onEntry(pEvent); } diff --git a/src/core/states/StateConnectCard.h b/src/core/states/StateConnectCard.h index 940275c..740730b 100644 --- a/src/core/states/StateConnectCard.h +++ b/src/core/states/StateConnectCard.h @@ -4,8 +4,9 @@ #pragma once -#include "AbstractGenericState.h" +#include "AbstractState.h" #include "command/CreateCardConnectionCommand.h" +#include "GenericContextContainer.h" class test_StateConnectCard; @@ -13,7 +14,8 @@ namespace governikus { class StateConnectCard - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; @@ -26,11 +28,12 @@ class StateConnectCard void onCardInserted(); void onCommandDone(QSharedPointer pCommand); void onReaderRemoved(const QString& pReaderName); - void onAbort(); + + public: + void onEntry(QEvent* pEvent) override; Q_SIGNALS: void fireRetry(); - void fireReaderRemoved(); }; diff --git a/src/core/states/StateDestroyPace.cpp b/src/core/states/StateDestroyPace.cpp index 19fe8a5..b5eb38a 100644 --- a/src/core/states/StateDestroyPace.cpp +++ b/src/core/states/StateDestroyPace.cpp @@ -9,7 +9,8 @@ using namespace governikus; StateDestroyPace::StateDestroyPace(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateDestroyPace.h b/src/core/states/StateDestroyPace.h index e5c9856..1bd97bd 100644 --- a/src/core/states/StateDestroyPace.h +++ b/src/core/states/StateDestroyPace.h @@ -6,9 +6,9 @@ #pragma once -#include "AbstractGenericState.h" - +#include "AbstractState.h" #include "context/ChangePinContext.h" +#include "GenericContextContainer.h" class test_StateDestroyPace; @@ -16,7 +16,8 @@ namespace governikus { class StateDestroyPace - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateDidAuthenticateEac1.cpp b/src/core/states/StateDidAuthenticateEac1.cpp index e2d59ce..4d92b25 100644 --- a/src/core/states/StateDidAuthenticateEac1.cpp +++ b/src/core/states/StateDidAuthenticateEac1.cpp @@ -13,7 +13,8 @@ using namespace governikus; StateDidAuthenticateEac1::StateDidAuthenticateEac1(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } @@ -63,3 +64,11 @@ void StateDidAuthenticateEac1::onCardCommandDone(QSharedPointer Q_EMIT fireAbort(); } } + + +void StateDidAuthenticateEac1::onEntry(QEvent* pEvent) +{ + //: INFO ALL_PLATFORMS Status message after the PIN was entered, Terminal Authentication. + getContext()->setProgress(20, tr("Service provider is being verified")); + AbstractState::onEntry(pEvent); +} diff --git a/src/core/states/StateDidAuthenticateEac1.h b/src/core/states/StateDidAuthenticateEac1.h index c2d2d1f..024942c 100644 --- a/src/core/states/StateDidAuthenticateEac1.h +++ b/src/core/states/StateDidAuthenticateEac1.h @@ -4,9 +4,10 @@ #pragma once +#include "AbstractState.h" #include "command/DidAuthenticateEAC1Command.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" class test_StateDidAuthenticateEac1; @@ -14,7 +15,8 @@ namespace governikus { class StateDidAuthenticateEac1 - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; @@ -26,6 +28,9 @@ class StateDidAuthenticateEac1 private Q_SLOTS: void onCardCommandDone(QSharedPointer pCommand); + public: + void onEntry(QEvent* pEvent) override; + }; } // namespace governikus diff --git a/src/core/states/StateDidAuthenticateEac2.cpp b/src/core/states/StateDidAuthenticateEac2.cpp index a27f11f..5f1000f 100644 --- a/src/core/states/StateDidAuthenticateEac2.cpp +++ b/src/core/states/StateDidAuthenticateEac2.cpp @@ -10,7 +10,8 @@ using namespace governikus; StateDidAuthenticateEac2::StateDidAuthenticateEac2(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } @@ -64,3 +65,11 @@ void StateDidAuthenticateEac2::onCardCommandDone(QSharedPointer Q_EMIT fireContinue(); } + + +void StateDidAuthenticateEac2::onEntry(QEvent* pEvent) +{ + //: INFO ALL_PLATFORMS Status message after the PIN was entered, Card Authentication. + getContext()->setProgress(40, tr("Card is being verified")); + AbstractState::onEntry(pEvent); +} diff --git a/src/core/states/StateDidAuthenticateEac2.h b/src/core/states/StateDidAuthenticateEac2.h index 3e4df1a..61ed0a6 100644 --- a/src/core/states/StateDidAuthenticateEac2.h +++ b/src/core/states/StateDidAuthenticateEac2.h @@ -4,24 +4,32 @@ #pragma once +#include "AbstractState.h" #include "command/DidAuthenticateEAC2Command.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" + +class test_StateDidAuthenticateEac2; namespace governikus { class StateDidAuthenticateEac2 - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; + friend class ::test_StateDidAuthenticateEac2; explicit StateDidAuthenticateEac2(const QSharedPointer& pContext); virtual void run() override; private Q_SLOTS: void onCardCommandDone(QSharedPointer pCommand); + + public: + void onEntry(QEvent* pEvent) override; }; } // namespace governikus diff --git a/src/core/states/StateDidList.cpp b/src/core/states/StateDidList.cpp index 9947bac..0cecb9c 100644 --- a/src/core/states/StateDidList.cpp +++ b/src/core/states/StateDidList.cpp @@ -10,7 +10,8 @@ using namespace governikus; StateDidList::StateDidList(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateDidList.h b/src/core/states/StateDidList.h index ebf206a..29099cb 100644 --- a/src/core/states/StateDidList.h +++ b/src/core/states/StateDidList.h @@ -4,14 +4,16 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateDidList - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateEACAdditionalInputType.cpp b/src/core/states/StateEACAdditionalInputType.cpp index 19b1571..6a8f756 100644 --- a/src/core/states/StateEACAdditionalInputType.cpp +++ b/src/core/states/StateEACAdditionalInputType.cpp @@ -7,7 +7,8 @@ using namespace governikus; StateEACAdditionalInputType::StateEACAdditionalInputType(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateEACAdditionalInputType.h b/src/core/states/StateEACAdditionalInputType.h index a21152e..bc5478a 100644 --- a/src/core/states/StateEACAdditionalInputType.h +++ b/src/core/states/StateEACAdditionalInputType.h @@ -4,14 +4,16 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateEACAdditionalInputType - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateEditAccessRights.cpp b/src/core/states/StateEditAccessRights.cpp index 3cbef70..5232020 100644 --- a/src/core/states/StateEditAccessRights.cpp +++ b/src/core/states/StateEditAccessRights.cpp @@ -4,15 +4,47 @@ #include "StateEditAccessRights.h" +Q_DECLARE_LOGGING_CATEGORY(statemachine) + using namespace governikus; StateEditAccessRights::StateEditAccessRights(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } void StateEditAccessRights::run() { + const auto& effectiveRights = getContext()->getEffectiveAccessRights(); + qCDebug(statemachine) << effectiveRights; + + const auto& requiredRights = getContext()->getRequiredAccessRights(); + printRights(QStringLiteral("Required rights"), requiredRights); + + const auto& optionalRights = getContext()->getOptionalAccessRights(); + printRights(QStringLiteral("Optional selected rights"), effectiveRights & optionalRights); + + printRights(QStringLiteral("Optional not selected rights"), optionalRights - effectiveRights); + Q_EMIT fireContinue(); } + + +void StateEditAccessRights::printRights(const QString& pTitle, const QSet& pRights) const +{ + QStringList accessRightsToDisplay; + if (pRights.isEmpty()) + { + accessRightsToDisplay += QStringLiteral("None"); + } + else + { + for (const auto& accessRight : pRights) + { + accessRightsToDisplay += AccessRoleAndRightsUtil::toDisplayText(accessRight); + } + } + qCDebug(statemachine) << pTitle << ":" << accessRightsToDisplay.join(QLatin1String(", ")); +} diff --git a/src/core/states/StateEditAccessRights.h b/src/core/states/StateEditAccessRights.h index a3b7577..4869d18 100644 --- a/src/core/states/StateEditAccessRights.h +++ b/src/core/states/StateEditAccessRights.h @@ -7,20 +7,24 @@ #pragma once -#include "AbstractGenericState.h" +#include "AbstractState.h" #include "context/AuthContext.h" +#include "GenericContextContainer.h" namespace governikus { class StateEditAccessRights - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; explicit StateEditAccessRights(const QSharedPointer& pContext); virtual void run() override; + + void printRights(const QString& pTitle, const QSet& pRights) const; }; } // namespace governikus diff --git a/src/core/states/StateEnterNewPacePin.cpp b/src/core/states/StateEnterNewPacePin.cpp index 5c43759..ec2ece2 100644 --- a/src/core/states/StateEnterNewPacePin.cpp +++ b/src/core/states/StateEnterNewPacePin.cpp @@ -9,7 +9,8 @@ using namespace governikus; StateEnterNewPacePin::StateEnterNewPacePin(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateEnterNewPacePin.h b/src/core/states/StateEnterNewPacePin.h index cc216b2..3839068 100644 --- a/src/core/states/StateEnterNewPacePin.h +++ b/src/core/states/StateEnterNewPacePin.h @@ -4,14 +4,16 @@ #pragma once +#include "AbstractState.h" #include "context/ChangePinContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateEnterNewPacePin - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateEnterPacePassword.cpp b/src/core/states/StateEnterPacePassword.cpp index 1ea922f..1629a1f 100644 --- a/src/core/states/StateEnterPacePassword.cpp +++ b/src/core/states/StateEnterPacePassword.cpp @@ -9,7 +9,8 @@ using namespace governikus; StateEnterPacePassword::StateEnterPacePassword(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateEnterPacePassword.h b/src/core/states/StateEnterPacePassword.h index 6c36810..397092a 100644 --- a/src/core/states/StateEnterPacePassword.h +++ b/src/core/states/StateEnterPacePassword.h @@ -4,14 +4,16 @@ #pragma once +#include "AbstractState.h" #include "context/WorkflowContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateEnterPacePassword - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateEstablishPaceChannel.cpp b/src/core/states/StateEstablishPaceChannel.cpp index 2b8abdb..9e6dddb 100644 --- a/src/core/states/StateEstablishPaceChannel.cpp +++ b/src/core/states/StateEstablishPaceChannel.cpp @@ -13,7 +13,8 @@ Q_DECLARE_LOGGING_CATEGORY(statemachine) using namespace governikus; StateEstablishPaceChannel::StateEstablishPaceChannel(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) , mPasswordId(PacePasswordId::UNKNOWN) { } @@ -32,7 +33,6 @@ void StateEstablishPaceChannel::run() QByteArray certificateDescription, effectiveChat; mPasswordId = getContext()->getEstablishPaceChannelType(); Q_ASSERT(mPasswordId != PacePasswordId::UNKNOWN); - getContext()->setEstablishPaceChannelType(PacePasswordId::UNKNOWN); if (mPasswordId == PacePasswordId::PACE_PIN || (mPasswordId == PacePasswordId::PACE_CAN && getContext()->isCanAllowedMode())) @@ -170,11 +170,6 @@ void StateEstablishPaceChannel::onEstablishConnectionDone(QSharedPointersetLastPaceResult(CardReturnCode::OK_PUK); - if (auto changePinContext = getContext().objectCast()) - { - changePinContext->setSuccessMessage(tr("PIN successfully unblocked")); - } - Q_EMIT firePacePukEstablished(); return; } diff --git a/src/core/states/StateEstablishPaceChannel.h b/src/core/states/StateEstablishPaceChannel.h index 50f422f..d0ce422 100644 --- a/src/core/states/StateEstablishPaceChannel.h +++ b/src/core/states/StateEstablishPaceChannel.h @@ -7,8 +7,9 @@ #pragma once -#include "AbstractGenericState.h" +#include "AbstractState.h" #include "context/WorkflowContext.h" +#include "GenericContextContainer.h" class test_StateEstablishPaceChannel; @@ -16,7 +17,8 @@ namespace governikus { class StateEstablishPaceChannel - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateExtractCvcsFromEac1InputType.cpp b/src/core/states/StateExtractCvcsFromEac1InputType.cpp index 8e7e256..a046b19 100644 --- a/src/core/states/StateExtractCvcsFromEac1InputType.cpp +++ b/src/core/states/StateExtractCvcsFromEac1InputType.cpp @@ -9,7 +9,8 @@ using namespace governikus; StateExtractCvcsFromEac1InputType::StateExtractCvcsFromEac1InputType(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateExtractCvcsFromEac1InputType.h b/src/core/states/StateExtractCvcsFromEac1InputType.h index 11d8237..dd28dfe 100644 --- a/src/core/states/StateExtractCvcsFromEac1InputType.h +++ b/src/core/states/StateExtractCvcsFromEac1InputType.h @@ -6,19 +6,20 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateExtractCvcsFromEac1InputType - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; - friend class ::test_StateExtractCvcsFromEac1InputType; explicit StateExtractCvcsFromEac1InputType(const QSharedPointer& pContext); virtual void run() override; diff --git a/src/core/states/StateGenericSendReceive.cpp b/src/core/states/StateGenericSendReceive.cpp index 7f5ced0..08813d6 100644 --- a/src/core/states/StateGenericSendReceive.cpp +++ b/src/core/states/StateGenericSendReceive.cpp @@ -22,7 +22,8 @@ using namespace governikus; StateGenericSendReceive::StateGenericSendReceive(const QSharedPointer& pContext, const QVector& pTypesToReceive, bool pConnectOnCardRemoved) - : AbstractGenericState(pContext, pConnectOnCardRemoved) + : AbstractState(pContext, pConnectOnCardRemoved) + , GenericContextContainer(pContext) , mTypesToReceive(pTypesToReceive) , mReply() { @@ -117,7 +118,7 @@ void StateGenericSendReceive::onSslHandshakeDone() switch (statusCode) { case GlobalStatus::Code::Workflow_TrustedChannel_Hash_Not_In_Description: - case GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description: + case GlobalStatus::Code::Workflow_Network_Ssl_Hash_Not_In_Certificate_Description: case GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Certificate_Unsupported_Algorithm_Or_Length: case GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length: { @@ -148,7 +149,7 @@ void StateGenericSendReceive::onSslHandshakeDone() else { qCCritical(network) << sessionFailedError; - updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error); + updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error); abort = true; } } @@ -311,7 +312,7 @@ void StateGenericSendReceive::onReplyFinished() if (paosHandler.getDetectedPaosType() == PaosType::UNKNOWN) { qCCritical(network) << "The program received an unknown message from the server."; - updateStatus(GlobalStatus::Code::Workflow_Unknown_Paos_Form_EidServer); + updateStatus(GlobalStatus::Code::Workflow_Unknown_Paos_From_EidServer); Q_EMIT fireAbort(); } else diff --git a/src/core/states/StateGenericSendReceive.h b/src/core/states/StateGenericSendReceive.h index c7732b4..c1a0def 100644 --- a/src/core/states/StateGenericSendReceive.h +++ b/src/core/states/StateGenericSendReceive.h @@ -4,11 +4,12 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" +#include "GenericContextContainer.h" #include "paos/invoke/PaosCreator.h" #include "paos/PaosMessage.h" #include "paos/PaosType.h" -#include "states/AbstractGenericState.h" #include #include @@ -20,7 +21,8 @@ namespace governikus { class StateGenericSendReceive - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT @@ -55,7 +57,7 @@ class StateSendStartPaos Q_OBJECT friend class StateBuilder; - StateSendStartPaos(const QSharedPointer& pContext) + explicit StateSendStartPaos(const QSharedPointer& pContext) : StateGenericSendReceive(pContext, QVector { PaosType::INITIALIZE_FRAMEWORK, PaosType::DID_LIST, @@ -115,7 +117,7 @@ class StateSendInitializeFrameworkResponse Q_OBJECT friend class StateBuilder; - StateSendInitializeFrameworkResponse(const QSharedPointer& pContext) + explicit StateSendInitializeFrameworkResponse(const QSharedPointer& pContext) : StateGenericSendReceive(pContext, QVector { PaosType::DID_LIST, PaosType::DID_AUTHENTICATE_EAC1, PaosType::STARTPAOS_RESPONSE @@ -169,7 +171,7 @@ class StateSendDIDListResponse Q_OBJECT friend class StateBuilder; - StateSendDIDListResponse(const QSharedPointer& pContext) + explicit StateSendDIDListResponse(const QSharedPointer& pContext) : StateGenericSendReceive(pContext, QVector { PaosType::DID_AUTHENTICATE_EAC1, PaosType::DISCONNECT, PaosType::STARTPAOS_RESPONSE @@ -222,7 +224,7 @@ class StateSendDIDAuthenticateResponseEAC1 Q_OBJECT friend class StateBuilder; - StateSendDIDAuthenticateResponseEAC1(const QSharedPointer& pContext) + explicit StateSendDIDAuthenticateResponseEAC1(const QSharedPointer& pContext) : StateGenericSendReceive(pContext, QVector { PaosType::DID_AUTHENTICATE_EAC2, PaosType::DISCONNECT, PaosType::STARTPAOS_RESPONSE @@ -275,7 +277,7 @@ class StateSendDIDAuthenticateResponseEACAdditionalInputType Q_OBJECT friend class StateBuilder; - StateSendDIDAuthenticateResponseEACAdditionalInputType(const QSharedPointer& pContext) + explicit StateSendDIDAuthenticateResponseEACAdditionalInputType(const QSharedPointer& pContext) : StateGenericSendReceive(pContext, QVector { PaosType::DID_AUTHENTICATE_EAC_ADDITIONAL_INPUT_TYPE, PaosType::STARTPAOS_RESPONSE @@ -323,7 +325,7 @@ class StateSendDIDAuthenticateResponseEAC2 Q_OBJECT friend class StateBuilder; - StateSendDIDAuthenticateResponseEAC2(const QSharedPointer& pContext) + explicit StateSendDIDAuthenticateResponseEAC2(const QSharedPointer& pContext) : StateGenericSendReceive(pContext, QVector { PaosType::TRANSMIT, PaosType::DISCONNECT, PaosType::STARTPAOS_RESPONSE @@ -376,7 +378,7 @@ class StateSendTransmitResponse Q_OBJECT friend class StateBuilder; - StateSendTransmitResponse(const QSharedPointer& pContext) + explicit StateSendTransmitResponse(const QSharedPointer& pContext) : StateGenericSendReceive(pContext, QVector { PaosType::TRANSMIT, PaosType::DISCONNECT, PaosType::STARTPAOS_RESPONSE @@ -430,7 +432,7 @@ class StateSendDisconnectResponse Q_OBJECT friend class StateBuilder; - StateSendDisconnectResponse(const QSharedPointer& pContext) + explicit StateSendDisconnectResponse(const QSharedPointer& pContext) : StateGenericSendReceive(pContext, PaosType::STARTPAOS_RESPONSE) { } @@ -457,7 +459,7 @@ class StateSendDisconnectResponse virtual void emitStateMachineSignal(int pResult) override { - Q_UNUSED(pResult); + Q_UNUSED(pResult) } diff --git a/src/core/states/StateGetSelfAuthenticationData.cpp b/src/core/states/StateGetSelfAuthenticationData.cpp index f3ad485..418b4b0 100644 --- a/src/core/states/StateGetSelfAuthenticationData.cpp +++ b/src/core/states/StateGetSelfAuthenticationData.cpp @@ -18,7 +18,8 @@ Q_DECLARE_LOGGING_CATEGORY(network) using namespace governikus; StateGetSelfAuthenticationData::StateGetSelfAuthenticationData(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) , mReply(nullptr) { } @@ -90,7 +91,7 @@ bool StateGetSelfAuthenticationData::checkSslConnectionAndSaveCertificate(const return false; case CertificateChecker::CertificateStatus::Hash_Not_In_Description: - reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description, issuerName)); + reportCommunicationError(GlobalStatus(GlobalStatus::Code::Workflow_Network_Ssl_Hash_Not_In_Certificate_Description, issuerName)); return false; } @@ -126,14 +127,14 @@ void StateGetSelfAuthenticationData::onNetworkReply() } else { - qDebug() << "No valid data of self authentication."; + qDebug() << "No valid data of self-authentication."; updateStatus(GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided); Q_EMIT fireAbort(); } } else { - qDebug() << "Could not read data for self authentication."; + qDebug() << "Could not read data for self-authentication."; updateStatus(GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided); Q_EMIT fireAbort(); } diff --git a/src/core/states/StateGetSelfAuthenticationData.h b/src/core/states/StateGetSelfAuthenticationData.h index 19a5567..947e0cd 100644 --- a/src/core/states/StateGetSelfAuthenticationData.h +++ b/src/core/states/StateGetSelfAuthenticationData.h @@ -6,18 +6,23 @@ #pragma once +#include "AbstractState.h" #include "context/SelfAuthContext.h" +#include "GenericContextContainer.h" #include "NetworkManager.h" -#include "states/AbstractGenericState.h" + +class test_StateGetSelfAuthenticationData; namespace governikus { class StateGetSelfAuthenticationData - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; + friend class ::test_StateGetSelfAuthenticationData; QPointer mReply; diff --git a/src/core/states/StateGetTcToken.cpp b/src/core/states/StateGetTcToken.cpp index 11acdd4..c7d3cf8 100644 --- a/src/core/states/StateGetTcToken.cpp +++ b/src/core/states/StateGetTcToken.cpp @@ -22,7 +22,8 @@ Q_DECLARE_LOGGING_CATEGORY(network) StateGetTcToken::StateGetTcToken(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) , mReply() { } @@ -118,7 +119,7 @@ void StateGetTcToken::onSslHandshakeDone() { mReply->abort(); qCritical() << "Error while connecting to the service provider. The server's SSL certificate uses an unsupported key algorithm or length."; - updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error); + updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error); Q_EMIT fireAbort(); return; } @@ -127,7 +128,7 @@ void StateGetTcToken::onSslHandshakeDone() { mReply->abort(); qCritical() << "Error while connecting to the service provider. The SSL connection uses an unsupported key algorithm or length."; - updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error); + updateStatus(GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error); Q_EMIT fireAbort(); return; } @@ -187,7 +188,7 @@ void StateGetTcToken::parseTcToken() Q_EMIT fireAbort(); return; } - const auto& tcToken = QSharedPointer::create(data); + const QSharedPointer& tcToken = QSharedPointer::create(data); getContext()->setTcToken(tcToken); getContext()->setTcTokenNotFound(!tcToken->isSchemaConform()); diff --git a/src/core/states/StateGetTcToken.h b/src/core/states/StateGetTcToken.h index c402138..6f6b2e4 100644 --- a/src/core/states/StateGetTcToken.h +++ b/src/core/states/StateGetTcToken.h @@ -6,19 +6,21 @@ #pragma once +#include "AbstractState.h" +#include "context/AuthContext.h" +#include "GenericContextContainer.h" + #include #include -#include "context/AuthContext.h" -#include "states/AbstractGenericState.h" - class test_StateGetTcToken; namespace governikus { class StateGetTcToken - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateInitializeFramework.cpp b/src/core/states/StateInitializeFramework.cpp index fd046f9..0becde2 100644 --- a/src/core/states/StateInitializeFramework.cpp +++ b/src/core/states/StateInitializeFramework.cpp @@ -8,7 +8,8 @@ using namespace governikus; StateInitializeFramework::StateInitializeFramework(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateInitializeFramework.h b/src/core/states/StateInitializeFramework.h index 9cca069..956d5cd 100644 --- a/src/core/states/StateInitializeFramework.h +++ b/src/core/states/StateInitializeFramework.h @@ -4,8 +4,9 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" #include @@ -13,7 +14,8 @@ namespace governikus { class StateInitializeFramework - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateLoadTcTokenUrl.cpp b/src/core/states/StateLoadTcTokenUrl.cpp index 490489a..6f548ed 100644 --- a/src/core/states/StateLoadTcTokenUrl.cpp +++ b/src/core/states/StateLoadTcTokenUrl.cpp @@ -10,7 +10,8 @@ using namespace governikus; StateLoadTcTokenUrl::StateLoadTcTokenUrl(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -19,8 +20,8 @@ void StateLoadTcTokenUrl::run() { const bool useTestUri = Env::getSingleton()->getGeneralSettings().useSelfAuthTestUri(); - const QUrl& url = SecureStorage::getInstance().getSelfAuthenticationUrl(useTestUri); - qDebug() << "Loaded tcTokenUrl for self authentication from securestorage:" << url; + const QUrl& url = Env::getSingleton()->getSelfAuthenticationUrl(useTestUri); + qDebug() << "Loaded tcTokenUrl for self-authentication from securestorage:" << url; getContext()->setTcTokenUrl(url); Q_EMIT fireContinue(); diff --git a/src/core/states/StateLoadTcTokenUrl.h b/src/core/states/StateLoadTcTokenUrl.h index f5dd3b0..58edff5 100644 --- a/src/core/states/StateLoadTcTokenUrl.h +++ b/src/core/states/StateLoadTcTokenUrl.h @@ -6,14 +6,16 @@ #pragma once +#include "AbstractState.h" #include "context/SelfAuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateLoadTcTokenUrl - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateMaintainCardConnection.cpp b/src/core/states/StateMaintainCardConnection.cpp index 9050a1c..155104d 100644 --- a/src/core/states/StateMaintainCardConnection.cpp +++ b/src/core/states/StateMaintainCardConnection.cpp @@ -13,7 +13,8 @@ using namespace governikus; StateMaintainCardConnection::StateMaintainCardConnection(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateMaintainCardConnection.h b/src/core/states/StateMaintainCardConnection.h index 01ebe6c..c65e99b 100644 --- a/src/core/states/StateMaintainCardConnection.h +++ b/src/core/states/StateMaintainCardConnection.h @@ -4,14 +4,16 @@ #pragma once +#include "AbstractState.h" #include "context/WorkflowContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateMaintainCardConnection - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateParseTcTokenUrl.cpp b/src/core/states/StateParseTcTokenUrl.cpp index db36d65..825d516 100644 --- a/src/core/states/StateParseTcTokenUrl.cpp +++ b/src/core/states/StateParseTcTokenUrl.cpp @@ -11,7 +11,8 @@ using namespace governikus; StateParseTcTokenUrl::StateParseTcTokenUrl(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateParseTcTokenUrl.h b/src/core/states/StateParseTcTokenUrl.h index 7085c6f..bc61077 100644 --- a/src/core/states/StateParseTcTokenUrl.h +++ b/src/core/states/StateParseTcTokenUrl.h @@ -6,14 +6,17 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" + namespace governikus { class StateParseTcTokenUrl - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StatePreVerification.cpp b/src/core/states/StatePreVerification.cpp index 4ed0af6..871a5bc 100644 --- a/src/core/states/StatePreVerification.cpp +++ b/src/core/states/StatePreVerification.cpp @@ -18,9 +18,10 @@ using namespace governikus; StatePreVerification::StatePreVerification(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) - , mTrustedCvcas(CVCertificate::fromHex(SecureStorage::getInstance().getCVRootCertificates(true)) - + CVCertificate::fromHex(SecureStorage::getInstance().getCVRootCertificates(false))) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) + , mTrustedCvcas(CVCertificate::fromHex(Env::getSingleton()->getCVRootCertificates(true)) + + CVCertificate::fromHex(Env::getSingleton()->getCVRootCertificates(false))) , mValidationDateTime(QDateTime::currentDateTime()) { } diff --git a/src/core/states/StatePreVerification.h b/src/core/states/StatePreVerification.h index 72c3793..5478889 100644 --- a/src/core/states/StatePreVerification.h +++ b/src/core/states/StatePreVerification.h @@ -6,8 +6,9 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" #include @@ -16,7 +17,8 @@ namespace governikus { class StatePreVerification - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StatePrepareChangePin.cpp b/src/core/states/StatePrepareChangePin.cpp index 8bf9052..81e8401 100644 --- a/src/core/states/StatePrepareChangePin.cpp +++ b/src/core/states/StatePrepareChangePin.cpp @@ -12,7 +12,8 @@ Q_DECLARE_LOGGING_CATEGORY(statemachine) StatePrepareChangePin::StatePrepareChangePin(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StatePrepareChangePin.h b/src/core/states/StatePrepareChangePin.h index 659eec5..4ffe45a 100644 --- a/src/core/states/StatePrepareChangePin.h +++ b/src/core/states/StatePrepareChangePin.h @@ -4,14 +4,16 @@ #pragma once +#include "AbstractState.h" #include "context/ChangePinContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StatePrepareChangePin - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StatePreparePace.cpp b/src/core/states/StatePreparePace.cpp index 51f16f7..f2fe030 100644 --- a/src/core/states/StatePreparePace.cpp +++ b/src/core/states/StatePreparePace.cpp @@ -11,13 +11,15 @@ using namespace governikus; StatePreparePace::StatePreparePace(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } void StatePreparePace::run() { + getContext()->setEstablishPaceChannelType(PacePasswordId::UNKNOWN); const QSharedPointer& cardConnection = getContext()->getCardConnection(); if (!cardConnection) { diff --git a/src/core/states/StatePreparePace.h b/src/core/states/StatePreparePace.h index 5b9dd62..ec2b3bf 100644 --- a/src/core/states/StatePreparePace.h +++ b/src/core/states/StatePreparePace.h @@ -4,14 +4,17 @@ #pragma once +#include "AbstractState.h" #include "context/WorkflowContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" + namespace governikus { class StatePreparePace - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateProcessCertificatesFromEac2.cpp b/src/core/states/StateProcessCertificatesFromEac2.cpp index 757bdb2..513f6a3 100644 --- a/src/core/states/StateProcessCertificatesFromEac2.cpp +++ b/src/core/states/StateProcessCertificatesFromEac2.cpp @@ -13,7 +13,8 @@ using namespace governikus; StateProcessCertificatesFromEac2::StateProcessCertificatesFromEac2(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateProcessCertificatesFromEac2.h b/src/core/states/StateProcessCertificatesFromEac2.h index 0b2eae0..443a42d 100644 --- a/src/core/states/StateProcessCertificatesFromEac2.h +++ b/src/core/states/StateProcessCertificatesFromEac2.h @@ -6,19 +6,20 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateProcessCertificatesFromEac2 - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; - friend class ::test_StateProcessCertificatesFromEac2; explicit StateProcessCertificatesFromEac2(const QSharedPointer& pContext); virtual void run() override; diff --git a/src/core/states/StateProcessing.cpp b/src/core/states/StateProcessing.cpp index b485ef9..93c2b82 100644 --- a/src/core/states/StateProcessing.cpp +++ b/src/core/states/StateProcessing.cpp @@ -7,7 +7,8 @@ using namespace governikus; StateProcessing::StateProcessing(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateProcessing.h b/src/core/states/StateProcessing.h index cb146f4..cdbcb55 100644 --- a/src/core/states/StateProcessing.h +++ b/src/core/states/StateProcessing.h @@ -6,14 +6,16 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateProcessing - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateRedirectBrowser.cpp b/src/core/states/StateRedirectBrowser.cpp index 290618f..ba98b7d 100644 --- a/src/core/states/StateRedirectBrowser.cpp +++ b/src/core/states/StateRedirectBrowser.cpp @@ -15,19 +15,24 @@ using namespace governikus; StateRedirectBrowser::StateRedirectBrowser(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } void StateRedirectBrowser::run() { +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + // Only skip redirects on mobile platforms because it induces a forced focus change if (getContext()->isSkipRedirect()) { qDebug() << "Skipping redirect, Workflow pending"; Q_EMIT fireContinue(); } - else if (getContext()->isTcTokenNotFound()) + else +#endif + if (getContext()->isTcTokenNotFound()) { sendErrorPage(HTTP_STATUS_NOT_FOUND); } @@ -38,13 +43,13 @@ void StateRedirectBrowser::run() else { bool redirectSuccess; - if (!getContext()->getStartPaosResult().isOk()) + if (getContext()->getStartPaosResult().isOk()) { - redirectSuccess = sendRedirect(getContext()->getRefreshUrl(), getContext()->getStartPaosResult()); + redirectSuccess = sendRedirect(getContext()->getRefreshUrl(), getContext()->getStatus()); } else { - redirectSuccess = sendRedirect(getContext()->getRefreshUrl(), getContext()->getStatus()); + redirectSuccess = sendRedirect(getContext()->getRefreshUrl(), getContext()->getStartPaosResult()); } if (redirectSuccess) diff --git a/src/core/states/StateRedirectBrowser.h b/src/core/states/StateRedirectBrowser.h index b6ed65a..07a291c 100644 --- a/src/core/states/StateRedirectBrowser.h +++ b/src/core/states/StateRedirectBrowser.h @@ -6,9 +6,10 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" #include "ECardApiResult.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" #include @@ -16,7 +17,8 @@ namespace governikus { class StateRedirectBrowser - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateSelectPasswordId.cpp b/src/core/states/StateSelectPasswordId.cpp index bfbb8ce..4a97ab1 100644 --- a/src/core/states/StateSelectPasswordId.cpp +++ b/src/core/states/StateSelectPasswordId.cpp @@ -9,7 +9,8 @@ using namespace governikus; StateSelectPasswordId::StateSelectPasswordId(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateSelectPasswordId.h b/src/core/states/StateSelectPasswordId.h index d23c1cc..3971581 100644 --- a/src/core/states/StateSelectPasswordId.h +++ b/src/core/states/StateSelectPasswordId.h @@ -4,21 +4,19 @@ #pragma once -#include "AbstractGenericState.h" - +#include "AbstractState.h" #include "context/WorkflowContext.h" - -class test_StateSelectPasswordId; +#include "GenericContextContainer.h" namespace governikus { class StateSelectPasswordId - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; - friend class ::test_StateSelectPasswordId; explicit StateSelectPasswordId(const QSharedPointer& pContext); virtual void run() override; diff --git a/src/core/states/StateSelectReader.cpp b/src/core/states/StateSelectReader.cpp index 0d054c3..a80bcd4 100644 --- a/src/core/states/StateSelectReader.cpp +++ b/src/core/states/StateSelectReader.cpp @@ -16,7 +16,8 @@ using namespace governikus; StateSelectReader::StateSelectReader(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -41,10 +42,16 @@ void StateSelectReader::run() if (!Env::getSingleton()->isUsedAsSDK()) { - const auto& readerPlugInTypes = getContext()->getReaderPlugInTypes(); + const auto& readerPlugInTypes = Enum::getList(); + const auto& enabledPlugInTypes = getContext()->getReaderPlugInTypes(); for (const auto t : readerPlugInTypes) { - readerManager->startScan(t); + if (enabledPlugInTypes.contains(t)) + { + readerManager->startScan(t); + continue; + } + readerManager->stopScan(t); } } } @@ -54,13 +61,28 @@ void StateSelectReader::onReaderInfoChanged() { const QSharedPointer context = getContext(); Q_ASSERT(context); + bool currentReaderHasEidCardButInsufficientApduLength = false; const QVector& plugInTypes = context->getReaderPlugInTypes(); - const auto allReaders = Env::getSingleton()->getReaderInfos(plugInTypes); - const QVector selectableReaders = filter([](const ReaderInfo& info) + const auto allReaders = Env::getSingleton()->getReaderInfos(ReaderFilter(plugInTypes)); + QVector selectableReaders; + + for (const auto& info : allReaders) + { + if (info.isConnected() && (!requiresCard(info.getPlugInType()) || info.hasEidCard())) + { + if (info.sufficientApduLength()) { - return info.isConnected() && (!requiresCard(info.getPlugInType()) || info.hasEidCard()); - }, allReaders); + selectableReaders.append(info); + } + else + { + currentReaderHasEidCardButInsufficientApduLength = true; + } + } + } + + context->setCurrentReaderHasEidCardButInsufficientApduLength(currentReaderHasEidCardButInsufficientApduLength); if (selectableReaders.isEmpty()) { @@ -79,26 +101,7 @@ void StateSelectReader::onReaderInfoChanged() } -void StateSelectReader::onAbort() -{ - const QSharedPointer context = getContext(); - Q_ASSERT(context); - - const auto& readerName = context->getReaderName(); - if (!readerName.isEmpty()) - { - const auto readerManager = Env::getSingleton(); - const ReaderInfo readerInfo = readerManager->getReaderInfo(readerName); - if (readerInfo.isConnected()) - { - readerManager->disconnectReader(readerName); - } - } - Q_EMIT fireRetry(); -} - - -void StateSelectReader::onReaderDeviceError(const GlobalStatus pErrorCode) +void StateSelectReader::onReaderDeviceError(const GlobalStatus& pErrorCode) { if (pErrorCode.isError() && !pErrorCode.is(GlobalStatus::Code::Workflow_Reader_Device_Scan_Error)) { @@ -113,13 +116,11 @@ void StateSelectReader::onEntry(QEvent* pEvent) const WorkflowContext* const context = getContext().data(); Q_ASSERT(context); - mConnections += connect(context, &WorkflowContext::fireAbortCardSelection, this, &StateSelectReader::onAbort); - /* * Note: the plugin types to be used in this state must be already set in the workflow context before this state is entered. * Changing the plugin types in the context, e.g. from {NFC} to {BLUETOOTH}, causes the state to be left with a fireRetry signal. */ mConnections += connect(context, &WorkflowContext::fireReaderPlugInTypesChanged, this, &StateSelectReader::fireRetry); - AbstractGenericState::onEntry(pEvent); + AbstractState::onEntry(pEvent); } diff --git a/src/core/states/StateSelectReader.h b/src/core/states/StateSelectReader.h index 98ab9b8..7d336bc 100644 --- a/src/core/states/StateSelectReader.h +++ b/src/core/states/StateSelectReader.h @@ -4,19 +4,19 @@ #pragma once -#include "AbstractGenericState.h" +#include "AbstractState.h" +#include "GenericContextContainer.h" -class test_StateSelectReader; namespace governikus { class StateSelectReader - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; - friend class ::test_StateSelectReader; private: explicit StateSelectReader(const QSharedPointer& pContext); @@ -26,8 +26,7 @@ class StateSelectReader private Q_SLOTS: void onReaderInfoChanged(); - void onAbort(); - void onReaderDeviceError(const GlobalStatus pError); + void onReaderDeviceError(const GlobalStatus& pError); public: void onEntry(QEvent* pEvent) override; diff --git a/src/core/states/StateSendWhitelistSurvey.cpp b/src/core/states/StateSendWhitelistSurvey.cpp index bed1e9c..ac1db76 100644 --- a/src/core/states/StateSendWhitelistSurvey.cpp +++ b/src/core/states/StateSendWhitelistSurvey.cpp @@ -6,7 +6,7 @@ #include "AppSettings.h" #include "ReaderManager.h" -#include "SurveyHandler.h" +#include "SurveyModel.h" Q_DECLARE_LOGGING_CATEGORY(statemachine) @@ -14,7 +14,8 @@ using namespace governikus; StateSendWhitelistSurvey::StateSendWhitelistSurvey(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -48,17 +49,7 @@ void StateSendWhitelistSurvey::run() return; } - const QString& readerName = authContext->getReaderName(); - if (readerName.isEmpty()) - { - qWarning() << "No reader information available, cannot send survey to whitelist server."; - Q_ASSERT(false); - Q_EMIT fireAbort(); - return; - } - - const ReaderInfo& readerInfo = Env::getSingleton()->getReaderInfo(readerName); - SurveyHandler().sendSurvey(readerInfo.getMaxApduLength()); + Env::getSingleton()->transmitSurvey(); #endif Q_EMIT fireContinue(); diff --git a/src/core/states/StateSendWhitelistSurvey.h b/src/core/states/StateSendWhitelistSurvey.h index 7cdd3e4..b2824d2 100644 --- a/src/core/states/StateSendWhitelistSurvey.h +++ b/src/core/states/StateSendWhitelistSurvey.h @@ -7,14 +7,17 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" +#include namespace governikus { class StateSendWhitelistSurvey - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateShowSelfInfo.cpp b/src/core/states/StateShowSelfInfo.cpp index a17e7f0..b292c71 100644 --- a/src/core/states/StateShowSelfInfo.cpp +++ b/src/core/states/StateShowSelfInfo.cpp @@ -8,7 +8,8 @@ using namespace governikus; StateShowSelfInfo::StateShowSelfInfo(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateShowSelfInfo.h b/src/core/states/StateShowSelfInfo.h index 3e3278d..261a861 100644 --- a/src/core/states/StateShowSelfInfo.h +++ b/src/core/states/StateShowSelfInfo.h @@ -4,14 +4,16 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateShowSelfInfo - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateStartPaos.cpp b/src/core/states/StateStartPaos.cpp index 92969a9..114da07 100644 --- a/src/core/states/StateStartPaos.cpp +++ b/src/core/states/StateStartPaos.cpp @@ -9,7 +9,8 @@ using namespace governikus; StateStartPaos::StateStartPaos(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateStartPaos.h b/src/core/states/StateStartPaos.h index eea8889..837db5d 100644 --- a/src/core/states/StateStartPaos.h +++ b/src/core/states/StateStartPaos.h @@ -6,14 +6,16 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateStartPaos - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateStartPaosResponse.cpp b/src/core/states/StateStartPaosResponse.cpp index 3bafdbb..c848af8 100644 --- a/src/core/states/StateStartPaosResponse.cpp +++ b/src/core/states/StateStartPaosResponse.cpp @@ -7,7 +7,8 @@ using namespace governikus; StateStartPaosResponse::StateStartPaosResponse(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateStartPaosResponse.h b/src/core/states/StateStartPaosResponse.h index 10f3a1b..0a80202 100644 --- a/src/core/states/StateStartPaosResponse.h +++ b/src/core/states/StateStartPaosResponse.h @@ -6,14 +6,16 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateStartPaosResponse - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateTransmit.cpp b/src/core/states/StateTransmit.cpp index 7ef36a2..283de89 100644 --- a/src/core/states/StateTransmit.cpp +++ b/src/core/states/StateTransmit.cpp @@ -8,7 +8,8 @@ using namespace governikus; StateTransmit::StateTransmit(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } @@ -49,3 +50,11 @@ void StateTransmit::onCardCommandDone(QSharedPointer pCommand) Q_EMIT fireAbort(); } } + + +void StateTransmit::onEntry(QEvent* pEvent) +{ + //: INFO ALL_PLATFORMS Status message after the PIN was entered, communication between eID server and card is running. + getContext()->setProgress(60, tr("Reading data")); + AbstractState::onEntry(pEvent); +} diff --git a/src/core/states/StateTransmit.h b/src/core/states/StateTransmit.h index 73b6c71..a61d3f4 100644 --- a/src/core/states/StateTransmit.h +++ b/src/core/states/StateTransmit.h @@ -6,9 +6,10 @@ #pragma once +#include "AbstractState.h" #include "command/TransmitCommand.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" class test_StateTransmit; @@ -16,7 +17,8 @@ namespace governikus { class StateTransmit - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; @@ -27,6 +29,9 @@ class StateTransmit private Q_SLOTS: friend class ::test_StateTransmit; void onCardCommandDone(QSharedPointer pCommand); + + public: + void onEntry(QEvent* pEvent) override; }; } // namespace governikus diff --git a/src/core/states/StateUnfortunateCardPosition.cpp b/src/core/states/StateUnfortunateCardPosition.cpp index c1ca4a1..d955859 100644 --- a/src/core/states/StateUnfortunateCardPosition.cpp +++ b/src/core/states/StateUnfortunateCardPosition.cpp @@ -2,14 +2,16 @@ * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany */ +#include "AbstractState.h" +#include "GenericContextContainer.h" #include "StateUnfortunateCardPosition.h" - using namespace governikus; StateUnfortunateCardPosition::StateUnfortunateCardPosition(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateUnfortunateCardPosition.h b/src/core/states/StateUnfortunateCardPosition.h index cb4a105..62770a9 100644 --- a/src/core/states/StateUnfortunateCardPosition.h +++ b/src/core/states/StateUnfortunateCardPosition.h @@ -4,14 +4,16 @@ #pragma once +#include "AbstractState.h" #include "context/WorkflowContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateUnfortunateCardPosition - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateUpdateRetryCounter.cpp b/src/core/states/StateUpdateRetryCounter.cpp index 6389055..a73d64b 100644 --- a/src/core/states/StateUpdateRetryCounter.cpp +++ b/src/core/states/StateUpdateRetryCounter.cpp @@ -14,7 +14,8 @@ using namespace governikus; StateUpdateRetryCounter::StateUpdateRetryCounter(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -46,7 +47,12 @@ void StateUpdateRetryCounter::onUpdateRetryCounterDone(QSharedPointergetCardConnection()->getReaderInfo().getRetryCounter() != -1 && "Retry counter must be intialized if command has succeeded."); +#ifndef QT_NO_DEBUG + if (getContext()->getCardConnection()->getReaderInfo().getRetryCounter() == -1) + { + qCWarning(statemachine) << "Retry counter should be intialized if command has succeeded."; + } +#endif Q_EMIT fireContinue(); } diff --git a/src/core/states/StateUpdateRetryCounter.h b/src/core/states/StateUpdateRetryCounter.h index 6cc5fa9..6ed9755 100644 --- a/src/core/states/StateUpdateRetryCounter.h +++ b/src/core/states/StateUpdateRetryCounter.h @@ -7,9 +7,9 @@ #pragma once -#include "AbstractGenericState.h" - +#include "AbstractState.h" #include "context/ChangePinContext.h" +#include "GenericContextContainer.h" class test_StateUpdateRetryCounter; @@ -17,7 +17,8 @@ namespace governikus { class StateUpdateRetryCounter - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateVerifyRetryCounter.cpp b/src/core/states/StateVerifyRetryCounter.cpp index df2d2e3..d6bef0b 100644 --- a/src/core/states/StateVerifyRetryCounter.cpp +++ b/src/core/states/StateVerifyRetryCounter.cpp @@ -11,7 +11,8 @@ using namespace governikus; StateVerifyRetryCounter::StateVerifyRetryCounter(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/StateVerifyRetryCounter.h b/src/core/states/StateVerifyRetryCounter.h index 8ee1943..fc83eba 100644 --- a/src/core/states/StateVerifyRetryCounter.h +++ b/src/core/states/StateVerifyRetryCounter.h @@ -4,14 +4,17 @@ #pragma once +#include "AbstractState.h" #include "context/WorkflowContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" + namespace governikus { class StateVerifyRetryCounter - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/StateWriteHistory.cpp b/src/core/states/StateWriteHistory.cpp index 54469a1..2f93db2 100644 --- a/src/core/states/StateWriteHistory.cpp +++ b/src/core/states/StateWriteHistory.cpp @@ -12,7 +12,8 @@ using namespace governikus; StateWriteHistory::StateWriteHistory(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -44,6 +45,7 @@ void StateWriteHistory::run() CVCertificateBody body = getContext()->getDidAuthenticateEac1()->getCvCertificates().at(0)->getBody(); QString effectiveDate = body.getCertificateEffectiveDate().toString(Qt::DefaultLocaleShortDate); QString expirationDate = body.getCertificateExpirationDate().toString(Qt::DefaultLocaleShortDate); + //: LABEL ALL_PLATFORMS QString validity = tr("Validity:\n%1 - %2").arg(effectiveDate, expirationDate); QStringList requestedData; @@ -68,3 +70,12 @@ void StateWriteHistory::run() } Q_EMIT fireContinue(); } + + +void StateWriteHistory::onEntry(QEvent* pEvent) +{ + //: INFO ALL_PLATFORMS Status message after the authentication was completed, the results are prepared for the user, mainly relevant for the self authentication since it takes some more time. + const auto text = getContext()->getStatus().isNoError() ? tr("Preparing results") : QString(); // The empty string is set to not confuse users when they get redirected to the service provider + getContext()->setProgress(100, text); + AbstractState::onEntry(pEvent); +} diff --git a/src/core/states/StateWriteHistory.h b/src/core/states/StateWriteHistory.h index b6c5c85..0cf8317 100644 --- a/src/core/states/StateWriteHistory.h +++ b/src/core/states/StateWriteHistory.h @@ -7,21 +7,26 @@ #pragma once +#include "AbstractState.h" #include "context/AuthContext.h" -#include "states/AbstractGenericState.h" +#include "GenericContextContainer.h" namespace governikus { class StateWriteHistory - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; explicit StateWriteHistory(const QSharedPointer& pContext); virtual void run() override; + + public: + void onEntry(QEvent* pEvent) override; }; } // namespace governikus diff --git a/src/core/states/remote_service/StateChangePinRemote.cpp b/src/core/states/remote_service/StateChangePinRemote.cpp index 8dde9f3..0d15322 100644 --- a/src/core/states/remote_service/StateChangePinRemote.cpp +++ b/src/core/states/remote_service/StateChangePinRemote.cpp @@ -16,7 +16,8 @@ using namespace governikus; StateChangePinRemote::StateChangePinRemote(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/remote_service/StateChangePinRemote.h b/src/core/states/remote_service/StateChangePinRemote.h index 8bcba09..c483faf 100644 --- a/src/core/states/remote_service/StateChangePinRemote.h +++ b/src/core/states/remote_service/StateChangePinRemote.h @@ -8,7 +8,9 @@ #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" + class test_StateChangePinRemote; @@ -16,7 +18,8 @@ namespace governikus { class StateChangePinRemote - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/remote_service/StateChangePinResponse.cpp b/src/core/states/remote_service/StateChangePinResponse.cpp index aabcbef..65a7a3e 100644 --- a/src/core/states/remote_service/StateChangePinResponse.cpp +++ b/src/core/states/remote_service/StateChangePinResponse.cpp @@ -11,7 +11,8 @@ using namespace governikus; StateChangePinResponse::StateChangePinResponse(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/remote_service/StateChangePinResponse.h b/src/core/states/remote_service/StateChangePinResponse.h index ba38a13..9df62d4 100644 --- a/src/core/states/remote_service/StateChangePinResponse.h +++ b/src/core/states/remote_service/StateChangePinResponse.h @@ -8,13 +8,15 @@ #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" namespace governikus { class StateChangePinResponse - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/remote_service/StateEnterNewPacePinRemote.cpp b/src/core/states/remote_service/StateEnterNewPacePinRemote.cpp index 2267b77..b6bbfab 100644 --- a/src/core/states/remote_service/StateEnterNewPacePinRemote.cpp +++ b/src/core/states/remote_service/StateEnterNewPacePinRemote.cpp @@ -9,7 +9,8 @@ using namespace governikus; StateEnterNewPacePinRemote::StateEnterNewPacePinRemote(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -39,5 +40,5 @@ void StateEnterNewPacePinRemote::onEntry(QEvent* pEvent) } mConnections += connect(getContext().data(), &RemoteServiceContext::fireCancelPasswordRequest, this, &StateEnterNewPacePinRemote::onCancelChangePin); - AbstractGenericState::onEntry(pEvent); + AbstractState::onEntry(pEvent); } diff --git a/src/core/states/remote_service/StateEnterNewPacePinRemote.h b/src/core/states/remote_service/StateEnterNewPacePinRemote.h index dcc1398..a6fd06a 100644 --- a/src/core/states/remote_service/StateEnterNewPacePinRemote.h +++ b/src/core/states/remote_service/StateEnterNewPacePinRemote.h @@ -5,13 +5,15 @@ #pragma once #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" namespace governikus { class StateEnterNewPacePinRemote - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/remote_service/StateEnterPacePasswordRemote.cpp b/src/core/states/remote_service/StateEnterPacePasswordRemote.cpp index 39749d6..4599144 100644 --- a/src/core/states/remote_service/StateEnterPacePasswordRemote.cpp +++ b/src/core/states/remote_service/StateEnterPacePasswordRemote.cpp @@ -9,7 +9,8 @@ using namespace governikus; StateEnterPacePasswordRemote::StateEnterPacePasswordRemote(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -42,5 +43,5 @@ void StateEnterPacePasswordRemote::onEntry(QEvent* pEvent) } mConnections += connect(getContext().data(), &RemoteServiceContext::fireCancelPasswordRequest, this, &StateEnterPacePasswordRemote::onCancelEstablishPaceChannel); - AbstractGenericState::onEntry(pEvent); + AbstractState::onEntry(pEvent); } diff --git a/src/core/states/remote_service/StateEnterPacePasswordRemote.h b/src/core/states/remote_service/StateEnterPacePasswordRemote.h index f8e8004..b1a155d 100644 --- a/src/core/states/remote_service/StateEnterPacePasswordRemote.h +++ b/src/core/states/remote_service/StateEnterPacePasswordRemote.h @@ -5,13 +5,15 @@ #pragma once #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" namespace governikus { class StateEnterPacePasswordRemote - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/remote_service/StateEstablishPaceChannelRemote.cpp b/src/core/states/remote_service/StateEstablishPaceChannelRemote.cpp index d4e2c1f..8d4e7e1 100644 --- a/src/core/states/remote_service/StateEstablishPaceChannelRemote.cpp +++ b/src/core/states/remote_service/StateEstablishPaceChannelRemote.cpp @@ -16,7 +16,8 @@ using namespace governikus; StateEstablishPaceChannelRemote::StateEstablishPaceChannelRemote(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/remote_service/StateEstablishPaceChannelRemote.h b/src/core/states/remote_service/StateEstablishPaceChannelRemote.h index 25c6fbd..09b3ad5 100644 --- a/src/core/states/remote_service/StateEstablishPaceChannelRemote.h +++ b/src/core/states/remote_service/StateEstablishPaceChannelRemote.h @@ -8,7 +8,8 @@ #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" class test_StateEstablishPaceChannelRemote; @@ -16,7 +17,8 @@ namespace governikus { class StateEstablishPaceChannelRemote - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/remote_service/StateEstablishPaceChannelResponse.cpp b/src/core/states/remote_service/StateEstablishPaceChannelResponse.cpp index 521cef3..824ac6b 100644 --- a/src/core/states/remote_service/StateEstablishPaceChannelResponse.cpp +++ b/src/core/states/remote_service/StateEstablishPaceChannelResponse.cpp @@ -10,7 +10,8 @@ using namespace governikus; StateEstablishPaceChannelResponse::StateEstablishPaceChannelResponse(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/remote_service/StateEstablishPaceChannelResponse.h b/src/core/states/remote_service/StateEstablishPaceChannelResponse.h index 6748b13..e29ee1d 100644 --- a/src/core/states/remote_service/StateEstablishPaceChannelResponse.h +++ b/src/core/states/remote_service/StateEstablishPaceChannelResponse.h @@ -8,15 +8,15 @@ #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" - -class test_StateEstablishPaceChannelRemote; +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" namespace governikus { class StateEstablishPaceChannelResponse - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/remote_service/StatePrepareChangePinRemote.cpp b/src/core/states/remote_service/StatePrepareChangePinRemote.cpp index 4dcb2fb..9e4066d 100644 --- a/src/core/states/remote_service/StatePrepareChangePinRemote.cpp +++ b/src/core/states/remote_service/StatePrepareChangePinRemote.cpp @@ -16,7 +16,8 @@ using namespace governikus; StatePrepareChangePinRemote::StatePrepareChangePinRemote(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/remote_service/StatePrepareChangePinRemote.h b/src/core/states/remote_service/StatePrepareChangePinRemote.h index 4781028..42aeed7 100644 --- a/src/core/states/remote_service/StatePrepareChangePinRemote.h +++ b/src/core/states/remote_service/StatePrepareChangePinRemote.h @@ -6,14 +6,16 @@ #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" namespace governikus { class StatePrepareChangePinRemote - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/remote_service/StatePreparePaceRemote.cpp b/src/core/states/remote_service/StatePreparePaceRemote.cpp index 1d88b5a..fd2abb3 100644 --- a/src/core/states/remote_service/StatePreparePaceRemote.cpp +++ b/src/core/states/remote_service/StatePreparePaceRemote.cpp @@ -16,7 +16,8 @@ using namespace governikus; StatePreparePaceRemote::StatePreparePaceRemote(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) { } @@ -56,8 +57,10 @@ void StatePreparePaceRemote::run() } break; - default: - Q_UNREACHABLE(); + case PacePasswordId::PACE_MRZ: + case PacePasswordId::UNKNOWN: + qCritical() << "Cannot handle PacePasswordId:" << parser.getPasswordId(); + break; } } diff --git a/src/core/states/remote_service/StatePreparePaceRemote.h b/src/core/states/remote_service/StatePreparePaceRemote.h index 36b5060..c0ec5e0 100644 --- a/src/core/states/remote_service/StatePreparePaceRemote.h +++ b/src/core/states/remote_service/StatePreparePaceRemote.h @@ -6,14 +6,16 @@ #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" namespace governikus { class StatePreparePaceRemote - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; diff --git a/src/core/states/remote_service/StateProcessRemoteMessages.cpp b/src/core/states/remote_service/StateProcessRemoteMessages.cpp index 68a9599..30ca318 100644 --- a/src/core/states/remote_service/StateProcessRemoteMessages.cpp +++ b/src/core/states/remote_service/StateProcessRemoteMessages.cpp @@ -14,7 +14,8 @@ using namespace governikus; StateProcessRemoteMessages::StateProcessRemoteMessages(const QSharedPointer& pContext) - : AbstractGenericState(pContext, false) + : AbstractState(pContext, false) + , GenericContextContainer(pContext) , mMessageConnections() { } @@ -31,6 +32,7 @@ void StateProcessRemoteMessages::run() const QSharedPointer server = context->getRemoteServer(); Q_ASSERT(server); + mConnections += connect(Env::getSingleton(), &ReaderManager::fireStatusChanged, this, &StateProcessRemoteMessages::onReaderStatusChanged); mConnections += connect(server.data(), &RemoteServer::fireMessageHandlerAdded, this, &StateProcessRemoteMessages::onMessageHandlerAdded); const auto messageHandler = server->getMessageHandler(); @@ -73,6 +75,26 @@ void StateProcessRemoteMessages::onClosed() } +void StateProcessRemoteMessages::onReaderStatusChanged(const ReaderManagerPlugInInfo& pInfo) +{ + if (pInfo.getPlugInType() != ReaderManagerPlugInType::NFC) + { + return; + } + + if (Env::getSingleton()->isScanRunning(ReaderManagerPlugInType::NFC)) + { + return; + } + + const auto& context = getContext(); + if (context->getRemoteServer()->isConnected()) + { + Q_EMIT fireAbort(); + } +} + + void StateProcessRemoteMessages::onEstablishPaceChannel(const QSharedPointer& pMessage, const QSharedPointer& pConnection) { Q_ASSERT(pMessage); diff --git a/src/core/states/remote_service/StateProcessRemoteMessages.h b/src/core/states/remote_service/StateProcessRemoteMessages.h index 5e826a4..2046840 100644 --- a/src/core/states/remote_service/StateProcessRemoteMessages.h +++ b/src/core/states/remote_service/StateProcessRemoteMessages.h @@ -9,7 +9,9 @@ #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "ReaderManager.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" class test_StateProcessRemoteMessages; @@ -17,7 +19,8 @@ namespace governikus { class StateProcessRemoteMessages - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; @@ -32,6 +35,7 @@ class StateProcessRemoteMessages private Q_SLOTS: void onMessageHandlerAdded(const QSharedPointer& pHandler); void onClosed(); + void onReaderStatusChanged(const ReaderManagerPlugInInfo& pInfo); void onModifyPin(const QSharedPointer& pMessage, const QSharedPointer& pConnection); void onEstablishPaceChannel(const QSharedPointer& pMessage, const QSharedPointer& pConnection); diff --git a/src/core/states/remote_service/StateStartRemoteService.cpp b/src/core/states/remote_service/StateStartRemoteService.cpp index f2529c0..2302975 100644 --- a/src/core/states/remote_service/StateStartRemoteService.cpp +++ b/src/core/states/remote_service/StateStartRemoteService.cpp @@ -11,7 +11,8 @@ using namespace governikus; StateStartRemoteService::StateStartRemoteService(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/remote_service/StateStartRemoteService.h b/src/core/states/remote_service/StateStartRemoteService.h index 681b555..e2f1385 100644 --- a/src/core/states/remote_service/StateStartRemoteService.h +++ b/src/core/states/remote_service/StateStartRemoteService.h @@ -8,19 +8,19 @@ #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" -class test_StateStartRemoteService; namespace governikus { class StateStartRemoteService - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; - friend class ::test_StateStartRemoteService; explicit StateStartRemoteService(const QSharedPointer& pContext); diff --git a/src/core/states/remote_service/StateStopRemoteService.cpp b/src/core/states/remote_service/StateStopRemoteService.cpp index cc8ce1d..fcd5ca0 100644 --- a/src/core/states/remote_service/StateStopRemoteService.cpp +++ b/src/core/states/remote_service/StateStopRemoteService.cpp @@ -10,7 +10,8 @@ using namespace governikus; StateStopRemoteService::StateStopRemoteService(const QSharedPointer& pContext) - : AbstractGenericState(pContext) + : AbstractState(pContext) + , GenericContextContainer(pContext) { } diff --git a/src/core/states/remote_service/StateStopRemoteService.h b/src/core/states/remote_service/StateStopRemoteService.h index 9deee03..d018c1a 100644 --- a/src/core/states/remote_service/StateStopRemoteService.h +++ b/src/core/states/remote_service/StateStopRemoteService.h @@ -8,19 +8,19 @@ #include "context/RemoteServiceContext.h" -#include "states/AbstractGenericState.h" +#include "states/AbstractState.h" +#include "states/GenericContextContainer.h" -class test_StateStopRemoteService; namespace governikus { class StateStopRemoteService - : public AbstractGenericState + : public AbstractState + , public GenericContextContainer { Q_OBJECT friend class StateBuilder; - friend class ::test_StateStopRemoteService; explicit StateStopRemoteService(const QSharedPointer& pContext); diff --git a/src/export/PdfCreator.cpp b/src/export/PdfCreator.cpp index 60320db..010b1cc 100644 --- a/src/export/PdfCreator.cpp +++ b/src/export/PdfCreator.cpp @@ -51,7 +51,8 @@ void PdfCreator::createHeader(const QString& pTitle, const QString& pHeadline) "").arg( pTitle, QCoreApplication::applicationName(), - tr("AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Ministry of the Interior, Building and Community."), + //: LABEL ALL_PLATFORMS + tr("AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Office for Information Security."), pHeadline); QSvgRenderer renderer(QStringLiteral(":/images/npa.svg")); @@ -74,6 +75,7 @@ void PdfCreator::createContent(const QString& pContent) void PdfCreator::createFooter() { const auto& footer = QStringLiteral("

%1

").arg( + //: LABEL ALL_PLATFORMS tr("For further information, please see https://www.ausweisapp.bund.de/")); mFooter.setHtml(footer); diff --git a/src/export/PdfExporter.cpp b/src/export/PdfExporter.cpp index 713328e..3525fef 100644 --- a/src/export/PdfExporter.cpp +++ b/src/export/PdfExporter.cpp @@ -108,25 +108,37 @@ bool PdfExporter::exportHistory() const auto& locale = LanguageLoader::getInstance().getUsedLocale(); - initTable(3, {180, 80}, {tr("Date"), tr("Details")}); + initTable(3, {180, 80}, + //: LABEL ALL_PLATFORMS + {tr("Date"), + //: LABEL ALL_PLATFORMS + tr("Details")}); + //: LABEL ALL_PLATFORMS const auto& dateTimeFormat = tr("dd.MM.yyyy hh:mm AP"); const auto& infos = Env::getSingleton()->getHistorySettings().getHistoryInfos(); for (const auto& entry : infos) { toggleRowColor(); const QString& dateTimeEntry = locale.toString(entry.getDateTime(), dateTimeFormat); + //: LABEL ALL_PLATFORMS addTableRow({dateTimeEntry, tr("Provider:"), entry.getSubjectName()}); + //: LABEL ALL_PLATFORMS addTableRow({QString(), tr("Purpose:"), entry.getPurpose()}); const auto& data = AccessRoleAndRightsUtil::joinFromTechnicalName(entry.getRequestedData()); + //: LABEL ALL_PLATFORMS addTableRow({QString(), tr("Data:"), data}); } closeTable(); const auto& now = QDateTime::currentDateTime(); + //: LABEL ALL_PLATFORMS QString date = locale.toString(now, tr("dd.MM.yyyy")); + //: LABEL ALL_PLATFORMS QString time = locale.toString(now, tr("hh:mm AP")); + //: LABEL ALL_PLATFORMS const auto& headline = tr("At %1 %2 the following data were saved:").arg(date, time); + //: LABEL ALL_PLATFORMS PdfCreator pdf(mFilename, tr("History"), headline, getContent()); const bool success = pdf.save(); checkOpenFile(success); @@ -142,7 +154,11 @@ bool PdfExporter::exportSelfInfo(const QDateTime& pDate, const QVector #include +static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE; + #ifndef ULLONG_MAX # define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ #endif @@ -49,6 +51,7 @@ #define SET_ERRNO(e) \ do { \ + parser->nread = nread; \ parser->http_errno = (e); \ } while(0) @@ -56,6 +59,7 @@ do { \ #define UPDATE_STATE(V) p_state = (enum state) (V); #define RETURN(V) \ do { \ + parser->nread = nread; \ parser->state = CURRENT_STATE(); \ return (V); \ } while (0); @@ -137,20 +141,20 @@ do { \ } while (0) /* Don't allow the total size of the HTTP headers (including the status - * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect + * line) to exceed max_header_size. This check is here to protect * embedders against denial-of-service attacks where the attacker feeds * us a never-ending header that the embedder keeps buffering. * * This check is arguably the responsibility of embedders but we're doing * it on the embedder's behalf because most won't bother and this way we - * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger + * make the web a little safer. max_header_size is still far bigger * than any reasonable request or response so this should never affect * day-to-day operation. */ #define COUNT_HEADER_SIZE(V) \ do { \ - parser->nread += (V); \ - if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ + nread += (uint32_t)(V); \ + if (UNLIKELY(nread > max_header_size)) { \ SET_ERRNO(HPE_HEADER_OVERFLOW); \ goto error; \ } \ @@ -192,7 +196,7 @@ static const char tokens[256] = { /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0, 0, 0, 0, 0, 0, 0, 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0, '!', 0, '#', '$', '%', '&', '\'', + ' ', '!', 0, '#', '$', '%', '&', '\'', /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 0, 0, '*', '+', 0, '-', '.', 0, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ @@ -312,6 +316,8 @@ enum state , s_req_http_HT , s_req_http_HTT , s_req_http_HTTP + , s_req_http_I + , s_req_http_IC , s_req_http_major , s_req_http_dot , s_req_http_minor @@ -419,14 +425,14 @@ enum http_host_state (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ (c) == '$' || (c) == ',') -#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) +#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c]) #if HTTP_PARSER_STRICT -#define TOKEN(c) (tokens[(unsigned char)c]) +#define TOKEN(c) STRICT_TOKEN(c) #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') #else -#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) +#define TOKEN(c) tokens[(unsigned char)c] #define IS_URL_CHAR(c) \ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) #define IS_HOST_CHAR(c) \ @@ -540,7 +546,7 @@ parse_url_char(enum state s, const char ch) return s_dead; } - /* FALLTHROUGH */ + /* fall through */ case s_req_server_start: case s_req_server: if (ch == '/') { @@ -644,6 +650,7 @@ size_t http_parser_execute (http_parser *parser, const char *status_mark = 0; enum state p_state = (enum state) parser->state; const unsigned int lenient = parser->lenient_http_headers; + uint32_t nread = parser->nread; /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { @@ -755,21 +762,16 @@ reexecute: case s_start_res: { + if (ch == CR || ch == LF) + break; parser->flags = 0; parser->content_length = ULLONG_MAX; - switch (ch) { - case 'H': - UPDATE_STATE(s_res_H); - break; - - case CR: - case LF: - break; - - default: - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; + if (ch == 'H') { + UPDATE_STATE(s_res_H); + } else { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; } CALLBACK_NOTIFY(message_begin); @@ -1086,11 +1088,17 @@ reexecute: case s_req_http_start: switch (ch) { + case ' ': + break; case 'H': UPDATE_STATE(s_req_http_H); break; - case ' ': - break; + case 'I': + if (parser->method == HTTP_SOURCE) { + UPDATE_STATE(s_req_http_I); + break; + } + /* fall through */ default: SET_ERRNO(HPE_INVALID_CONSTANT); goto error; @@ -1112,6 +1120,16 @@ reexecute: UPDATE_STATE(s_req_http_HTTP); break; + case s_req_http_I: + STRICT_CHECK(ch != 'C'); + UPDATE_STATE(s_req_http_IC); + break; + + case s_req_http_IC: + STRICT_CHECK(ch != 'E'); + UPDATE_STATE(s_req_http_HTTP); /* Treat "ICE" as "HTTP". */ + break; + case s_req_http_HTTP: STRICT_CHECK(ch != '/'); UPDATE_STATE(s_req_http_major); @@ -1238,8 +1256,14 @@ reexecute: break; switch (parser->header_state) { - case h_general: + case h_general: { + size_t left = data + len - p; + const char* pe = p + MIN(left, max_header_size); + while (p+1 < pe && TOKEN(p[1])) { + p++; + } break; + } case h_C: parser->index++; @@ -1339,13 +1363,14 @@ reexecute: } } - COUNT_HEADER_SIZE(p - start); - if (p == data + len) { --p; + COUNT_HEADER_SIZE(p - start); break; } + COUNT_HEADER_SIZE(p - start); + if (ch == ':') { UPDATE_STATE(s_header_value_discard_ws); CALLBACK_DATA(header_field); @@ -1369,7 +1394,7 @@ reexecute: break; } - /* FALLTHROUGH */ + /* fall through */ case s_header_value_start: { @@ -1411,6 +1436,11 @@ reexecute: parser->header_state = h_content_length_num; break; + /* when obsolete line folding is encountered for content length + * continue to the s_header_value state */ + case h_content_length_ws: + break; + case h_connection: /* looking for 'Connection: keep-alive' */ if (c == 'k') { @@ -1466,29 +1496,25 @@ reexecute: switch (h_state) { case h_general: - { - const char* p_cr; - const char* p_lf; - size_t limit = data + len - p; + { + size_t left = data + len - p; + const char* pe = p + MIN(left, max_header_size); - limit = MIN(limit, HTTP_MAX_HEADER_SIZE); - - p_cr = (const char*) memchr(p, CR, limit); - p_lf = (const char*) memchr(p, LF, limit); - if (p_cr != NULL) { - if (p_lf != NULL && p_cr >= p_lf) - p = p_lf; - else - p = p_cr; - } else if (UNLIKELY(p_lf != NULL)) { - p = p_lf; - } else { - p = data + len; + for (; p != pe; p++) { + ch = *p; + if (ch == CR || ch == LF) { + --p; + break; + } + if (!lenient && !IS_HEADER_CHAR(ch)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + } + if (p == data + len) + --p; + break; } - --p; - - break; - } case h_connection: case h_transfer_encoding: @@ -1498,7 +1524,7 @@ reexecute: case h_content_length: if (ch == ' ') break; h_state = h_content_length_num; - /* FALLTHROUGH */ + /* fall through */ case h_content_length_num: { @@ -1634,10 +1660,10 @@ reexecute: } parser->header_state = h_state; - COUNT_HEADER_SIZE(p - start); - if (p == data + len) --p; + + COUNT_HEADER_SIZE(p - start); break; } @@ -1655,6 +1681,10 @@ reexecute: case s_header_value_lws: { if (ch == ' ' || ch == '\t') { + if (parser->header_state == h_content_length_num) { + /* treat obsolete line folding as space */ + parser->header_state = h_content_length_ws; + } UPDATE_STATE(s_header_value_start); REEXECUTE(); } @@ -1707,6 +1737,11 @@ reexecute: case h_transfer_encoding_chunked: parser->flags |= F_CHUNKED; break; + case h_content_length: + /* do not allow empty content length */ + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + break; default: break; } @@ -1770,7 +1805,7 @@ reexecute: case 2: parser->upgrade = 1; - /* FALLTHROUGH */ + /* fall through */ case 1: parser->flags |= F_SKIPBODY; break; @@ -1794,6 +1829,7 @@ reexecute: STRICT_CHECK(ch != LF); parser->nread = 0; + nread = 0; hasBody = parser->flags & F_CHUNKED || (parser->content_length > 0 && parser->content_length != ULLONG_MAX); @@ -1888,7 +1924,7 @@ reexecute: case s_chunk_size_start: { - assert(parser->nread == 1); + assert(nread == 1); assert(parser->flags & F_CHUNKED); unhex_val = unhex[(unsigned char)ch]; @@ -1956,6 +1992,7 @@ reexecute: STRICT_CHECK(ch != LF); parser->nread = 0; + nread = 0; if (parser->content_length == 0) { parser->flags |= F_TRAILING; @@ -2002,6 +2039,7 @@ reexecute: assert(parser->flags & F_CHUNKED); STRICT_CHECK(ch != LF); parser->nread = 0; + nread = 0; UPDATE_STATE(s_chunk_size_start); CALLBACK_NOTIFY(chunk_complete); break; @@ -2013,7 +2051,7 @@ reexecute: } } - /* Run callbacks for any marks that we have leftover after we ran our of + /* Run callbacks for any marks that we have leftover after we ran out of * bytes. There should be at most one of these set, so it's OK to invoke * them in series (unset marks will not result in callbacks). * @@ -2095,6 +2133,16 @@ http_method_str (enum http_method m) return ELEM_AT(method_strings, m, ""); } +const char * +http_status_str (enum http_status s) +{ + switch (s) { +#define XX(num, name, string) case HTTP_STATUS_##name: return #string; + HTTP_STATUS_MAP(XX) +#undef XX + default: return ""; + } +} void http_parser_init (http_parser *parser, enum http_parser_type t) @@ -2155,7 +2203,7 @@ http_parse_host_char(enum http_host_state s, const char ch) { return s_http_host; } - /* FALLTHROUGH */ + /* fall through */ case s_http_host_v6_end: if (ch == ':') { return s_http_host_port_start; @@ -2168,7 +2216,7 @@ http_parse_host_char(enum http_host_state s, const char ch) { return s_http_host_v6_end; } - /* FALLTHROUGH */ + /* fall through */ case s_http_host_v6_start: if (IS_HEX(ch) || ch == ':' || ch == '.') { return s_http_host_v6; @@ -2184,7 +2232,7 @@ http_parse_host_char(enum http_host_state s, const char ch) { return s_http_host_v6_end; } - /* FALLTHROUGH */ + /* fall through */ case s_http_host_v6_zone_start: /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || @@ -2230,14 +2278,14 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { switch(new_s) { case s_http_host: if (s != s_http_host) { - u->field_data[UF_HOST].off = p - buf; + u->field_data[UF_HOST].off = (uint16_t)(p - buf); } u->field_data[UF_HOST].len++; break; case s_http_host_v6: if (s != s_http_host_v6) { - u->field_data[UF_HOST].off = p - buf; + u->field_data[UF_HOST].off = (uint16_t)(p - buf); } u->field_data[UF_HOST].len++; break; @@ -2249,7 +2297,7 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { case s_http_host_port: if (s != s_http_host_port) { - u->field_data[UF_PORT].off = p - buf; + u->field_data[UF_PORT].off = (uint16_t)(p - buf); u->field_data[UF_PORT].len = 0; u->field_set |= (1 << UF_PORT); } @@ -2258,7 +2306,7 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { case s_http_userinfo: if (s != s_http_userinfo) { - u->field_data[UF_USERINFO].off = p - buf ; + u->field_data[UF_USERINFO].off = (uint16_t)(p - buf); u->field_data[UF_USERINFO].len = 0; u->field_set |= (1 << UF_USERINFO); } @@ -2303,6 +2351,10 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, enum http_parser_url_fields uf, old_uf; int found_at = 0; + if (buflen == 0) { + return 1; + } + u->port = u->field_set = 0; s = is_connect ? s_req_server_start : s_req_spaces_before_url; old_uf = UF_MAX; @@ -2330,7 +2382,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, case s_req_server_with_at: found_at = 1; - /* FALLTHROUGH */ + /* fall through */ case s_req_server: uf = UF_HOST; break; @@ -2358,7 +2410,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, continue; } - u->field_data[uf].off = p - buf; + u->field_data[uf].off = (uint16_t)(p - buf); u->field_data[uf].len = 1; u->field_set |= (1 << uf); @@ -2421,6 +2473,7 @@ http_parser_pause(http_parser *parser, int paused) { */ if (HTTP_PARSER_ERRNO(parser) == HPE_OK || HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { + uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */ SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); } else { assert(0 && "Attempting to pause parser in error state"); @@ -2438,3 +2491,8 @@ http_parser_version(void) { HTTP_PARSER_VERSION_MINOR * 0x00100 | HTTP_PARSER_VERSION_PATCH * 0x00001; } + +void +http_parser_set_max_header_size(uint32_t size) { + max_header_size = size; +} diff --git a/src/external/http_parser/http_parser.h b/src/external/http_parser/http_parser.h index a0de71e..16b5281 100644 --- a/src/external/http_parser/http_parser.h +++ b/src/external/http_parser/http_parser.h @@ -26,8 +26,8 @@ extern "C" { /* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 8 -#define HTTP_PARSER_VERSION_PATCH 1 +#define HTTP_PARSER_VERSION_MINOR 9 +#define HTTP_PARSER_VERSION_PATCH 2 #include #if defined(_WIN32) && !defined(__MINGW32__) && \ @@ -407,6 +407,9 @@ int http_should_keep_alive(const http_parser *parser); /* Returns a string version of the HTTP method. */ const char *http_method_str(enum http_method m); +/* Returns a string version of the HTTP status code. */ +const char *http_status_str(enum http_status s); + /* Return a string name of the given error */ const char *http_errno_name(enum http_errno err); @@ -427,6 +430,9 @@ void http_parser_pause(http_parser *parser, int paused); /* Checks if this is the final chunk of the body. */ int http_body_is_final(const http_parser *parser); +/* Change the maximum header size provided at compile time. */ +void http_parser_set_max_header_size(uint32_t size); + #ifdef __cplusplus } #endif diff --git a/src/file_provider/Downloader.cpp b/src/file_provider/Downloader.cpp index ee3c2c1..247a594 100644 --- a/src/file_provider/Downloader.cpp +++ b/src/file_provider/Downloader.cpp @@ -5,10 +5,15 @@ #include "Downloader.h" #include "LogHandler.h" -#include "ScopeGuard.h" #include "SingletonHelper.h" #include "TlsChecker.h" +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include +#else +#include "ScopeGuard.h" +#endif + #include #include #include @@ -103,7 +108,7 @@ void Downloader::onNetworkReplyFinished() { qCDebug(fileprovider) << "Downloader finished:" << mCurrentReply->request().url().fileName(); - const ScopeGuard guard([this] { + const auto guard = qScopeGuard([this] { mCurrentReply->deleteLater(); mCurrentReply = nullptr; startDownloadIfPending(); diff --git a/src/file_provider/UpdatableFile.cpp b/src/file_provider/UpdatableFile.cpp index d1cc816..740c222 100644 --- a/src/file_provider/UpdatableFile.cpp +++ b/src/file_provider/UpdatableFile.cpp @@ -79,7 +79,7 @@ QString UpdatableFile::cachePath() const QUrl UpdatableFile::updateUrl(const QString& pSection, const QString& pName) { - const QUrl updateServerBaseUrl = SecureStorage::getInstance().getUpdateServerBaseUrl(); + const QUrl updateServerBaseUrl = Env::getSingleton()->getUpdateServerBaseUrl(); const QString section = pSection.isEmpty() ? QString() : Sep + pSection; return QUrl(updateServerBaseUrl.toString() + section + Sep + pName); @@ -148,7 +148,7 @@ QString UpdatableFile::makeSectionCachePath(const QString& pSection) void UpdatableFile::cleanupAfterUpdate(const std::function& pCustomAction) { - Downloader* const downloader = Env::getSingleton(); + auto* const downloader = Env::getSingleton(); disconnect(downloader, &Downloader::fireDownloadSuccess, this, &UpdatableFile::onDownloadSuccess); disconnect(downloader, &Downloader::fireDownloadFailed, this, &UpdatableFile::onDownloadFailed); disconnect(downloader, &Downloader::fireDownloadUnnecessary, this, &UpdatableFile::onDownloadUnnecessary); @@ -255,7 +255,7 @@ QUrl UpdatableFile::lookupUrl() } else { - return QStringLiteral("file://") + path; + return QStringLiteral("file:///") + path; } } @@ -310,7 +310,7 @@ void UpdatableFile::update() { mUpdateRunning = true; - Downloader* const downloader = Env::getSingleton(); + auto* const downloader = Env::getSingleton(); connect(downloader, &Downloader::fireDownloadSuccess, this, &UpdatableFile::onDownloadSuccess); connect(downloader, &Downloader::fireDownloadFailed, this, &UpdatableFile::onDownloadFailed); connect(downloader, &Downloader::fireDownloadUnnecessary, this, &UpdatableFile::onDownloadUnnecessary); diff --git a/src/global/BuildHelper.cpp b/src/global/BuildHelper.cpp index 44eff10..4cc678c 100644 --- a/src/global/BuildHelper.cpp +++ b/src/global/BuildHelper.cpp @@ -135,7 +135,7 @@ QByteArrayList BuildHelper::getAppCertificates(const QString& pPackageName) jbyteArray data = bytes.object(); const auto size = env->GetArrayLength(data); jbyte* buffer = env->GetByteArrayElements(data, 0); - list << QByteArray(reinterpret_cast(buffer), size).toHex(); + list << QByteArray(reinterpret_cast(buffer), size).toHex(); } if (env->ExceptionCheck()) diff --git a/src/global/CardReturnCode.cpp b/src/global/CardReturnCode.cpp index 6519217..eb33ee5 100644 --- a/src/global/CardReturnCode.cpp +++ b/src/global/CardReturnCode.cpp @@ -74,6 +74,7 @@ GlobalStatus CardReturnCodeUtil::toGlobalStatus(CardReturnCode pCode) } Q_UNREACHABLE(); + return GlobalStatus::Code::Unknown_Error; } diff --git a/src/global/DeviceInfo.cpp b/src/global/DeviceInfo.cpp index 895a93c..b41ed20 100644 --- a/src/global/DeviceInfo.cpp +++ b/src/global/DeviceInfo.cpp @@ -23,7 +23,7 @@ DeviceInfo::~DeviceInfo() #ifdef Q_OS_ANDROID -QString DeviceInfo::getField(const char* pField) +QString DeviceInfo::getField(const char* const pField) { QAndroidJniObject field = QAndroidJniObject::getStaticObjectField("android/os/Build", pField, "Ljava/lang/String;"); if (field == nullptr || !field.isValid()) diff --git a/src/global/DeviceInfo.h b/src/global/DeviceInfo.h index 6ef8ed1..d2fe7ac 100644 --- a/src/global/DeviceInfo.h +++ b/src/global/DeviceInfo.h @@ -20,7 +20,7 @@ class DeviceInfo Q_DISABLE_COPY(DeviceInfo) #ifdef Q_OS_ANDROID - static QString getField(const char* pField); + static QString getField(const char* const pField); #endif public: diff --git a/src/global/ECardApiResult.cpp b/src/global/ECardApiResult.cpp index f2efee6..bd5b6a6 100644 --- a/src/global/ECardApiResult.cpp +++ b/src/global/ECardApiResult.cpp @@ -69,7 +69,7 @@ QMap ECardApiResult::cConversionMap2 ECardApiResult ECardApiResult::fromStatus(const GlobalStatus& pStatus) { - if (pStatus.getStatusCode() == GlobalStatus::Code::No_Error || pStatus.getStatusCode() == GlobalStatus::Code::RemoteReader_CloseCode_NormalClose) + if (pStatus.getStatusCode() == GlobalStatus::Code::No_Error) { return createOk(); } @@ -101,7 +101,6 @@ void ECardApiResult::initConversionMaps() addConversionElement(GlobalStatus::Code::Card_Unexpected_Transmit_Status, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::No_Error, Minor::null); - addConversionElement(GlobalStatus::Code::RemoteReader_CloseCode_NormalClose, Minor::null); addConversionElement(GlobalStatus::Code::Workflow_Card_Removed, Minor::IFDL_CancellationByUser); @@ -124,7 +123,7 @@ void ECardApiResult::initConversionMaps() addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_ServiceUnavailable, Minor::DP_Trusted_Channel_Establishment_Failed); addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_TimeOut, Minor::DP_Trusted_Channel_Establishment_Failed); addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Proxy_Error, Minor::DP_Trusted_Channel_Establishment_Failed); - addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error, Minor::DP_Trusted_Channel_Establishment_Failed); + addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error, Minor::DP_Trusted_Channel_Establishment_Failed); addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Server_Format_Error, Minor::DP_Trusted_Channel_Establishment_Failed); addConversionElement(GlobalStatus::Code::Workflow_TrustedChannel_Other_Network_Error, Minor::DP_Trusted_Channel_Establishment_Failed); @@ -140,14 +139,13 @@ void ECardApiResult::initConversionMaps() addConversionElement(GlobalStatus::Code::Workflow_Redirect_Transmission_Error, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Workflow_Processing_Error, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Workflow_Reader_Became_Inaccessible, Minor::AL_Communication_Error); - addConversionElement(GlobalStatus::Code::Workflow_Reader_Communication_Error, Minor::AL_Communication_Error); - addConversionElement(GlobalStatus::Code::Workflow_Reader_Device_Connection_Error, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Bluetooth_Reader_Connection_Error, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Workflow_Reader_Device_Scan_Error, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Network_Ssl_Establishment_Error, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length, Minor::AL_Communication_Error); - addConversionElement(GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description, Minor::AL_Communication_Error); + addConversionElement(GlobalStatus::Code::Workflow_Network_Ssl_Hash_Not_In_Certificate_Description, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Workflow_Network_Empty_Redirect_Url, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Workflow_Network_Expected_Redirect, Minor::AL_Communication_Error); addConversionElement(GlobalStatus::Code::Workflow_Network_Invalid_Scheme, Minor::AL_Communication_Error); @@ -173,7 +171,7 @@ void ECardApiResult::initConversionMaps() addConversionElement(GlobalStatus::Code::Paos_Error_AL_Internal_Error, Minor::AL_Internal_Error); addConversionElement(GlobalStatus::Code::Workflow_Cannot_Confirm_IdCard_Authenticity, Minor::AL_Internal_Error); - addConversionElement(GlobalStatus::Code::Workflow_Unknown_Paos_Form_EidServer, Minor::AL_Internal_Error); + addConversionElement(GlobalStatus::Code::Workflow_Unknown_Paos_From_EidServer, Minor::AL_Internal_Error); addConversionElement(GlobalStatus::Code::Workflow_Unexpected_Message_From_EidServer, Minor::AL_Internal_Error); addConversionElement(GlobalStatus::Code::Workflow_Pin_Blocked_And_Puk_Objectionable, Minor::AL_Internal_Error); addConversionElement(GlobalStatus::Code::Card_Protocol_Error, Minor::AL_Internal_Error); @@ -207,9 +205,7 @@ void ECardApiResult::initConversionMaps() addConversionElement(GlobalStatus::Code::Downloader_Data_Corrupted, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose, Minor::AL_Unknown_Error); - addConversionElement(GlobalStatus::Code::RemoteReader_CloseCode_Undefined, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::RemoteConnector_InvalidRequest, Minor::AL_Unknown_Error); - addConversionElement(GlobalStatus::Code::RemoteConnector_EmptyPassword, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::RemoteConnector_NoSupportedApiLevel, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::RemoteConnector_ConnectionTimeout, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::RemoteConnector_ConnectionError, Minor::AL_Unknown_Error); @@ -347,90 +343,119 @@ QString ECardApiResult::getMessage(Minor pMinor) switch (pMinor) { case Minor::AL_Unknown_Error: + //: LABEL ALL_PLATFORMS return tr("An unexpected error has occurred during processing."); case Minor::AL_No_Permission: + //: LABEL ALL_PLATFORMS return tr("Use of the function by the client application is not permitted."); case Minor::AL_Internal_Error: + //: LABEL ALL_PLATFORMS return tr("An internal error has occurred during processing."); case Minor::AL_Parameter_Error: + //: LABEL ALL_PLATFORMS return tr("There was some problem with a provided or omitted parameter."); case Minor::AL_Unkown_API_Function: + //: LABEL ALL_PLATFORMS return tr("The API function is unknown."); case Minor::AL_Not_Initialized: + //: LABEL ALL_PLATFORMS return tr("The framework or layer has not been initialized."); case Minor::AL_Warning_Connection_Disconnected: + //: LABEL ALL_PLATFORMS return tr("The active session has been terminated."); case Minor::AL_Session_Terminated_Warning: + //: LABEL ALL_PLATFORMS return tr("The active session has been terminated."); case Minor::AL_Communication_Error: + //: LABEL ALL_PLATFORMS return tr("A Communication error occurred during processing."); case Minor::DP_Timeout_Error: + //: LABEL ALL_PLATFORMS return tr("The operation was terminated as the set time was exceeded."); case Minor::DP_Unknown_Channel_Handle: + //: LABEL ALL_PLATFORMS return tr("The operation was aborted as an invalid channel handle was used."); case Minor::DP_Communication_Error: + //: LABEL ALL_PLATFORMS return tr("A Communication error occurred during processing."); case Minor::DP_Trusted_Channel_Establishment_Failed: + //: LABEL ALL_PLATFORMS return tr("A trusted channel could not be opened."); case Minor::DP_Unknown_Protocol: + //: LABEL ALL_PLATFORMS return tr("The operation was aborted as an unknown protocol was used."); case Minor::DP_Unknown_Cipher_Suite: + //: LABEL ALL_PLATFORMS return tr("The operation was aborted as an unknown cipher suite was used."); case Minor::DP_Unknown_Webservice_Binding: + //: LABEL ALL_PLATFORMS return tr("The operation was aborted as an unknown web service binding was used."); case Minor::DP_Node_Not_Reachable: + //: LABEL ALL_PLATFORMS return tr("A Communication error occurred during processing."); case Minor::IFDL_Timeout_Error: + //: LABEL ALL_PLATFORMS return tr("The operation was terminated as the set time was exceeded."); case Minor::IFDL_Terminal_NoCard: + //: LABEL ALL_PLATFORMS return tr("The card is missing or was removed."); case Minor::IFDL_IO_RepeatedDataMismatch: + //: LABEL ALL_PLATFORMS return tr("The new PIN and the confirmation do not match."); case Minor::IFDL_IO_UnknownPINFormat: + //: LABEL ALL_PLATFORMS return tr("The format of the PIN is wrong."); case Minor::KEY_KeyGenerationNotPossible: + //: LABEL ALL_PLATFORMS return tr("Signature certificate key generation is not possible."); case Minor::SAL_Cancellation_by_User: + //: LABEL ALL_PLATFORMS return tr("The process was cancelled by the user."); case Minor::IL_Signature_InvalidCertificatePath: + //: LABEL ALL_PLATFORMS return tr("One or more certificate checks failed. The operation will be aborted due to security reasons."); case Minor::SAL_Invalid_Key: + //: LABEL ALL_PLATFORMS return tr("This action cannot be performed. The online identification function of your ID card is deactivated. Please contact the authority responsible for issuing your identification document to activate the online identification function."); case Minor::SAL_SecurityConditionNotSatisfied: + //: LABEL ALL_PLATFORMS return tr("The authenticity of your ID card could not be verified. Please make sure that you are using a genuine ID card. Please note that test applications require the use of a test ID card."); case Minor::SAL_MEAC_AgeVerificationFailedWarning: + //: LABEL ALL_PLATFORMS return tr("The age verification failed."); case Minor::SAL_MEAC_CommunityVerificationFailedWarning: + //: LABEL ALL_PLATFORMS return tr("The community verification failed."); case Minor::SAL_MEAC_DocumentValidityVerificationFailed: + //: LABEL ALL_PLATFORMS return tr("The ID card is invalid or disabled."); case Minor::null: diff --git a/src/global/EnumHelper.h b/src/global/EnumHelper.h index 116d9b9..9ce095d 100644 --- a/src/global/EnumHelper.h +++ b/src/global/EnumHelper.h @@ -44,6 +44,11 @@ namespace governikus inline bool operator!=(std::underlying_type::type pType, enumName pName)\ {\ return !(pType == pName);\ + }\ +\ + inline uint qHash(enumName pKey, uint pSeed)\ + {\ + return ::qHash(static_cast::type>(pKey), pSeed);\ } @@ -77,7 +82,7 @@ template class Enum using EnumBaseTypeT = typename std::underlying_type::type; private: - Enum(); + Enum() = delete; Q_DISABLE_COPY(Enum) public: @@ -96,7 +101,7 @@ template class Enum static QLatin1String getName(EnumTypeT pType) { const int value = static_cast(pType); - const char* name = getQtEnumMetaEnum().valueToKey(value); + const char* const name = getQtEnumMetaEnum().valueToKey(value); if (Q_UNLIKELY(name == nullptr)) { qCritical().noquote().nospace() << "CRITICAL CONVERSION MISMATCH: UNKNOWN 0x" << QString::number(value, 16); @@ -113,23 +118,22 @@ template class Enum } - static const QVector& getList() + static QVector getList() { - static QVector list; - if (list.isEmpty()) + QVector list; + + const QMetaEnum metaEnum = getQtEnumMetaEnum(); + list.reserve(metaEnum.keyCount()); + for (int i = 0; i < metaEnum.keyCount(); ++i) { - const QMetaEnum metaEnum = getQtEnumMetaEnum(); - list.reserve(metaEnum.keyCount()); - for (int i = 0; i < metaEnum.keyCount(); ++i) - { - list << static_cast(metaEnum.value(i)); - } + list << static_cast(metaEnum.value(i)); } + return list; } - static EnumTypeT fromString(const char* pValue, EnumTypeT pDefault) + static EnumTypeT fromString(const char* const pValue, EnumTypeT pDefault) { bool ok = false; int key = getQtEnumMetaEnum().keyToValue(pValue, &ok); diff --git a/src/global/Env.h b/src/global/Env.h index f4885c1..de04796 100644 --- a/src/global/Env.h +++ b/src/global/Env.h @@ -75,13 +75,8 @@ class Env const std::function mFunc; public: - FuncWrapper(const std::function& pFunc) - : mFunc(pFunc) - { - } - - - virtual ~FuncWrapper() + FuncWrapper(std::function pFunc) + : mFunc(std::move(pFunc)) { } @@ -107,17 +102,17 @@ class Env static Env& getInstance(); template - typename std::enable_if::value && std::is_destructible::value, T*>::type fetchRealSingleton() + inline T* fetchRealSingleton() { - static_assert(std::has_virtual_destructor::value, "Destructor must be virtual"); - return singleton(); - } - - - template - typename std::enable_if::value || !std::is_destructible::value, T*>::type fetchRealSingleton() - { - return &T::getInstance(); + if constexpr (std::is_abstract::value && std::is_destructible::value) + { + static_assert(std::has_virtual_destructor::value, "Destructor must be virtual"); + return singleton(); + } + else + { + return &T::getInstance(); + } } @@ -129,7 +124,7 @@ class Env inline typename std::enable_if::Value, T*>::type checkObjectInfo(Identifier pId, T* pObject) const #endif { - Q_UNUSED(pId); + Q_UNUSED(pId) return pObject; } @@ -174,34 +169,27 @@ class Env template - inline typename std::enable_if::type, Args ...>::value, T>::type newObject(Args&& ... pArgs) const + inline T newObject(Args&& ... pArgs) const { - static_assert(std::is_pointer::value, "It is impossible to return implementation of interface by value. Use pointer or add constructor!"); - auto obj = createNewObject(std::forward(pArgs) ...); - Q_ASSERT(obj); - return obj; - } - - - template - inline typename std::enable_if::value, T>::type internalNewObject(Args&& ... pArgs) const - { - using t = typename std::remove_pointer::type; - return new t(std::forward(pArgs) ...); - } - - - template - inline typename std::enable_if::value, T>::type internalNewObject(Args&& ... pArgs) const - { - return T(std::forward(pArgs) ...); - } - - - template - inline typename std::enable_if::type, Args ...>::value, T>::type newObject(Args&& ... pArgs) const - { - return internalNewObject(std::forward(pArgs) ...); + if constexpr (std::is_constructible::type, Args ...>::value) + { + if constexpr (std::is_pointer::value) + { + using t = typename std::remove_pointer::type; + return new t(std::forward(pArgs) ...); + } + else + { + return T(std::forward(pArgs) ...); + } + } + else + { + static_assert(std::is_pointer::value, "It is impossible to return implementation of interface by value. Use pointer or add constructor!"); + auto obj = createNewObject(std::forward(pArgs) ...); + Q_ASSERT(obj); + return obj; + } } @@ -307,11 +295,11 @@ class Env template - static void setCreator(const std::function& pFunc) + static void setCreator(std::function pFunc) { Q_ASSERT(pFunc); - const auto& value = QSharedPointer >::create(pFunc); + const auto& value = QSharedPointer >::create(std::move(pFunc)); auto& holder = getInstance(); const QWriteLocker locker(&holder.mLock); diff --git a/src/global/FileDestination.h b/src/global/FileDestination.h index 79d2290..88be709 100644 --- a/src/global/FileDestination.h +++ b/src/global/FileDestination.h @@ -41,12 +41,6 @@ class FileDestination } - static QString getPath(const char* pFilename) - { - return getPath() % QLatin1Char('/') % QString::fromUtf8(pFilename); - } - - }; } // namespace governikus diff --git a/src/global/GlobalStatus.cpp b/src/global/GlobalStatus.cpp index e059ead..4a12059 100644 --- a/src/global/GlobalStatus.cpp +++ b/src/global/GlobalStatus.cpp @@ -26,7 +26,7 @@ bool GlobalStatus::isMessageMasked() const { switch (d->mStatusCode) { - case Code::Workflow_Unknown_Paos_Form_EidServer: + case Code::Workflow_Unknown_Paos_From_EidServer: case Code::Workflow_Unexpected_Message_From_EidServer: case Code::Workflow_Preverification_Error: case Code::Workflow_No_Unique_AtCvc: @@ -47,7 +47,6 @@ bool GlobalStatus::isMessageMasked() const case Code::Workflow_TrustedChannel_Proxy_Error: case Code::Workflow_TrustedChannel_Server_Format_Error: case Code::Network_Ssl_Establishment_Error: - case Code::Workflow_TrustedChannel_Ssl_Establishment_Error: case Code::Workflow_Wrong_Parameter_Invocation: case Code::Network_Other_Error: case Code::Workflow_TrustedChannel_Other_Network_Error: @@ -56,6 +55,13 @@ bool GlobalStatus::isMessageMasked() const case Code::Workflow_Network_Expected_Redirect: case Code::Workflow_Network_Invalid_Scheme: case Code::Workflow_Network_Malformed_Redirect_Url: + case Code::Paos_Unexpected_Warning: + case Code::Paos_Error_AL_Unknown_Error: + case Code::Paos_Error_AL_Internal_Error: + case Code::Paos_Error_AL_Communication_Error: + case Code::Paos_Error_DP_Trusted_Channel_Establishment_Failed: + case Code::Paos_Error_SAL_Cancellation_by_User: + case Code::Paos_Error_SAL_Invalid_Key: return true; default: @@ -86,12 +92,15 @@ QString GlobalStatus::toErrorDescription(const bool pSimplifiedVersion) const { if (pSimplifiedVersion && isMessageMasked()) { + //: LABEL ALL_PLATFORMS const QString supportUrl = tr("https://www.ausweisapp.bund.de/en/qa/support/"); const QString hyperlink = QStringLiteral("").arg(supportUrl); #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + //: ERROR ANDROID IOS Error message which is used for "masked" errors. Generic message with link to support section of the homepage. QString message = tr("An error occurred. Please contact our %1support%2.").arg(hyperlink, QStringLiteral("")); #else + //: ERROR DESKTOP Error message which is used for "masked" errors. Generic message with link to support section of the homepage. QString message = tr("An error occurred. Please contact our %1support%2 or feel free to send us an email.").arg(hyperlink, QStringLiteral("")); #endif return message; @@ -108,54 +117,71 @@ QString GlobalStatus::toErrorDescriptionInternal() const return QString(); case Code::No_Error: + //: ERROR ALL_PLATFORMS No actual error occured, required to provide a message for status code No_Error. return tr("No error occurred."); case Code::Unknown_Error: + //: ERROR ALL_PLATFORMS An unknown error from any of the subcomponents (PCSC, EcardAPI, QtNetwork, ...) occurred. return tr("An unexpected error has occurred during processing."); case Code::Workflow_AlreadyInProgress_Error: + //: ERROR ALL_PLATFORMS An ActivationHandler is requested even though an operation is currently still running. return tr("Cannot start authentication. An operation is already in progress."); case Code::Workflow_Card_Removed: - return tr("The ID card has been removed. The process is aborted."); + //: ERROR ALL_PLATFORMS The card was removed after the PACE channel was established. + return tr("The connection to the ID card has been lost. The process was aborted."); case Code::Workflow_Cannot_Confirm_IdCard_Authenticity: + //: ERROR ALL_PLATFORMS The certificates supplied by the card did not pass the authenticity check, further operation is not allowed. return tr("The authenticity of your ID card could not be confirmed."); - case Code::Workflow_Unknown_Paos_Form_EidServer: + case Code::Workflow_Unknown_Paos_From_EidServer: + //: ERROR_MASKED ALL_PLATFORMS The type of a POAS message could not be determined. return tr("The program received an unknown message from the server."); case Code::Workflow_Unexpected_Message_From_EidServer: + //: ERROR_MASKED ALL_PLATFORMS The server sent a valid PAOS message but its type was unexpected. return tr("The program received an unexpected message from the server."); case Code::Workflow_Pin_Blocked_And_Puk_Objectionable: + //: ERROR ALL_PLATFORMS The id card was blocked after three wrongfully entered PINs, the PUK is required to unlock the card. Hint to do so in the PIN management section of the app. return tr("After three wrong entries your PIN is blocked. Please use the PIN management in this app to unblock it with the help of your PUK."); case Code::Workflow_Preverification_Developermode_Error: + //: ERROR ALL_PLATFORMS The developer mode is enabled but a productive environment was detected. return tr("Using the developer mode is only allowed in a test environment."); case Code::Workflow_Preverification_Error: + //: ERROR_MASKED ALL_PLATFORMS The certificates submitted by the server failed the authenticity check during pre-verification. return tr("Pre-verification failed."); case Code::Workflow_No_Unique_AtCvc: + //: ERROR_MASKED ALL_PLATFORMS The EAC1 request contained more than one At certificate. return tr("No unique AT CVC"); case Code::Workflow_No_Unique_DvCvc: + //: ERROR_MASKED ALL_PLATFORMS The EAC1 request contained more than one Dv certificate. return tr("No unique DV CVC"); case Code::Workflow_No_Permission_Error: + //: ERROR ALL_PLATFORMS DidAuthenticateEAC2, AA2 or the id card declined the certificates. return tr("Authentication failed."); case Code::Workflow_Certificate_No_Description: + //: ERROR_MASKED ALL_PLATFORMS return tr("No certificate description available."); case Code::Workflow_Certificate_No_Url_In_Description: + //: ERROR_MASKED ALL_PLATFORMS return tr("No subject url available in certificate description."); case Code::Workflow_Certificate_Hash_Error: + //: ERROR_MASKED ALL_PLATFORMS return tr("The certificate description does not match the certificate."); case Code::Workflow_Certificate_Sop_Error: + //: ERROR_MASKED ALL_PLATFORMS return tr("The subject URL in the certificate description and the TCToken URL don't satisfy the same origin policy."); case Code::Workflow_Error_Page_Transmission_Error: @@ -163,75 +189,88 @@ QString GlobalStatus::toErrorDescriptionInternal() const case Code::Workflow_Redirect_Transmission_Error: return getExternalInfo(); - case Code::Workflow_TrustedChannel_Establishment_Error: - return tr("Failed to establish secure connection"); - case Code::Workflow_TrustedChannel_Error_From_Server: + //: ERROR_MASKED ALL_PLATFORMS return tr("The program received an error from the server."); case Code::Workflow_TrustedChannel_Hash_Not_In_Description: - case Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description: + case Code::Workflow_Network_Ssl_Hash_Not_In_Certificate_Description: + //: ERROR ALL_PLATFORMS The TLS certificate was not folded with the Authorization Certificate, thus violating the security requirements. Might also be caused by a firewall and/or the antivirus software. return tr("Hash of certificate not in certificate description (issuer: %1). This indicates a misconfiguration or manipulation of the certificate. Please check that your antivirus-software and firewalls are not interfering with SSL traffic.").arg(getExternalInfo()); case Code::Workflow_TrustedChannel_No_Data_Received: + //: ERROR_MASKED ALL_PLATFORMS Received an empty TC token. return tr("Received no data."); case Code::Workflow_TrustedChannel_ServiceUnavailable: case Code::Network_ServiceUnavailable: + //: ERROR ALL_PLATFORMS A server has responded with an HTTP error code 503. return tr("The service is temporarily not available. Please try again later."); case Code::Network_TimeOut: case Code::Workflow_TrustedChannel_TimeOut: + //: ERROR_MASKED ALL_PLATFORMS The TCP connection to the server timed out. return tr("Establishing a connection is taking too long."); case Code::Network_Proxy_Error: case Code::Workflow_TrustedChannel_Proxy_Error: + //: ERROR_MASKED ALL_PLATFORMS The connection using a proxy failed, it may be misconfigured.. return tr("Establishing a connection via the proxy did not succeed."); case Code::Workflow_TrustedChannel_Server_Format_Error: + //: ERROR_MASKED ALL_PLATFORMS An critical error while retrieving or parsing the TC token occurred. return tr("It wasn't possible to connect to the server: the server sent a non-standard response."); case Code::Network_Ssl_Establishment_Error: - case Code::Workflow_TrustedChannel_Ssl_Establishment_Error: + case Code::Workflow_TrustedChannel_Establishment_Error: + //: ERROR_MASKED ALL_PLATFORMS Critical error from Qt's TLS API. return tr("It wasn't possible to connect to the server: a secure connection could not be established."); case Code::Workflow_Wrong_Parameter_Invocation: + //: ERROR_MASKED ALL_PLATFORMS The TC token URL could not be parsed/validated. return tr("Application was invoked with wrong parameters."); case Code::Network_Other_Error: case Code::Workflow_TrustedChannel_Other_Network_Error: + //: ERROR_MASKED ALL_PLATFORMS Other critial network error by Qt. return tr("An unknown network error occurred."); case Code::Workflow_Reader_Became_Inaccessible: + //: ERROR ALL_PLATFORMS The card reader was removed after the PACE channel was established. return tr("The selected card reader cannot be accessed anymore."); - case Code::Workflow_Reader_Communication_Error: - return tr("An error occurred while communicating with the card reader."); - case Code::Workflow_Server_Incomplete_Information_Provided: + //: ERROR ALL_PLATFORMS The result of the self authentication could not be retrieved from the eService. return tr("The server provided no or incomplete information. Your personal data could not be read out."); case Code::Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length: + //: ERROR_MASKED ALL_PLATFORMS return tr("Error while connecting to the service provider. The SSL connection uses an unsupported key algorithm or length."); case Code::Workflow_TrustedChannel_Ssl_Certificate_Unsupported_Algorithm_Or_Length: case Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length: + //: ERROR ALL_PLATFORMS Received a TLS certificate that uses an invalid algorithm or key length. return tr("Error while connecting to the server. The SSL certificate uses an unsupported key algorithm or length. Certificate issuer: %1").arg(getExternalInfo()); case Code::Workflow_Network_Empty_Redirect_Url: + //: ERROR_MASKED ALL_PLATFORMS The redirect URL could not be determined because the server sent an empty response. return tr("Empty redirect URL"); case Code::Workflow_Network_Expected_Redirect: + //: ERROR_MASKED ALL_PLATFORMS The redirect URL could not be determined due to an erroneous HTTP code. return tr("Expected redirect, got %1").arg(getExternalInfo()); case Code::Workflow_Network_Invalid_Scheme: + //: ERROR_MASKED ALL_PLATFORMS The redirect URL could not be determined because the redirect URL did not adhere to the HTTPS scheme. return tr("Invalid scheme: %1").arg(getExternalInfo()); case Code::Workflow_Network_Malformed_Redirect_Url: + //: ERROR_MASKED ALL_PLATFORMS The redirect URL could not be determined because the redirect URL was invalid. return tr("Malformed redirect URL: %1").arg(getExternalInfo()); case Code::Workflow_Cancellation_By_User: case Code::Card_Cancellation_By_User: + //: ERROR ALL_PLATFORMS The user cancelled the authentication in either the UI or the card reader. return tr("The process was cancelled by the user."); case Code::Paos_Generic_Server_Error: @@ -245,89 +284,104 @@ QString GlobalStatus::toErrorDescriptionInternal() const return getExternalInfo(); case Code::Card_Input_TimeOut: + //: ERROR ALL_PLATFORMS The card reader signalled that it timed out. return tr("The maximum time was exceeded during input process."); case Code::Card_Not_Found: + //: ERROR ALL_PLATFORMS Internal error, either PCSC, SaK or card reader could not find the id card. return tr("Card does not exist"); case Code::Card_Communication_Error: + //: ERROR ALL_PLATFORMS Communication with the card failed due to the specification of the TR (Technische Richtlinie), return tr("An error occurred while communicating with the ID card. Please make sure that your ID card is placed correctly on the card reader and try again."); case Code::Card_Protocol_Error: case Code::Card_Unexpected_Transmit_Status: + //: ERROR ALL_PLATFORMS Communication with the card failed due to the specification of the TR (Technische Richtlinie). The protocol was faulty or invalid values were requested/received, return QStringLiteral("%1 %3.").arg( tr("A protocol error occurred. Please make sure that your ID card is placed correctly on the card reader and try again. If the problem occurs again, please contact our support at"), tr("https://www.ausweisapp.bund.de/en/qa/support/"), tr("AusweisApp2 Support")); case Code::Card_Invalid_Pin: + //: ERROR ALL_PLATFORMS The id card declined the PIN. return tr("The given PIN is invalid."); case Code::Card_Invalid_Can: + //: ERROR ALL_PLATFORMS The id card declined the CAN. return tr("The given card access number (CAN) is invalid."); case Code::Card_Invalid_Puk: + //: ERROR ALL_PLATFORMS The id card declined the PUK. return tr("The given PUK is invalid."); case Code::Card_Pin_Blocked: + //: ERROR ALL_PLATFORMS The id card refused the PIN since the PIN feature is blocked after too many wrong attemps. return tr("The PIN was blocked after too many unsuccessful attempts."); case Code::Card_Pin_Not_Blocked: + //: ERROR ALL_PLATFORMS It was attempted to unlock the id card via PUK even though it was not locked in the first place. This scenario is avoided in the UI by hiding the respective UI elements. return tr("The PIN is not blocked."); case Code::Card_Puk_Blocked: + //: ERROR ALL_PLATFORMS The card declined the PUK since it was entered wrongfully 10 times, the local authorities have to be contacted to unlock the id card. return tr("The PUK was used ten times and is set inoperative. Please contact the competent authority that issued your ID document to unlock the PIN."); case Code::Card_NewPin_Mismatch: + //: ERROR ALL_PLATFORMS The card reader signalled that the new PIN was not confirmed correctly. return tr("The new PIN and the confirmation do not match."); case Code::Card_NewPin_Invalid_Length: + //: ERROR ALL_PLATFORMS The card reader declined the new PIN since its length was invalid. return tr("The length of the new PIN is not valid."); - case Code::Workflow_Reader_Device_Connection_Error: + case Code::Workflow_Bluetooth_Reader_Connection_Error: + //: ERROR ALL_PLATFORMS Error while connecting to a bluetooth card reader. return tr("An error occurred while connecting to a reader device."); case Code::Workflow_Reader_Device_Scan_Error: + //: ERROR ALL_PLATFORMS Error while searching for bluetooth card reader. return tr("An error occurred while scanning for reader devices."); - case Code::RemoteReader_CloseCode_NormalClose: - return tr("The remote reader connection was closed properly."); - case Code::RemoteReader_CloseCode_AbnormalClose: + //: ERROR ALL_PLATFORMS The connection to the smartphone card reader (SaK) was lost. return tr("The remote card reader connection was not closed properly."); - case Code::RemoteReader_CloseCode_Undefined: - return tr("Undefined error code occured when the remote card reader connection was closed."); - case Code::RemoteConnector_InvalidRequest: + //: ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) was invalid (missing device ID). return tr("Remote reader connection request contains invalid parameters."); - case Code::RemoteConnector_EmptyPassword: - return tr("Empty password in extended encryption of remote reader connection request."); - case Code::RemoteConnector_NoSupportedApiLevel: + //: ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) was invalid (API mismatch). return tr("Your remote reader version is incompatible with the local version. Please install the latest AusweisApp2 version on both your smartphone and your computer."); case Code::RemoteConnector_ConnectionTimeout: + //: ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) timed out. return tr("A timeout occurred while trying to establish a connection to a remote reader."); case Code::RemoteConnector_ConnectionError: + //: ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) failed due to network errors (Host not found, OS error, ...) return tr("An error occurred while trying to establish a connection to a remote reader."); case Code::RemoteConnector_RemoteHostRefusedConnection: + //: ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) was rejected by the device. return tr("Remote device has rejected the connection. Please check the pairing code."); case Code::Downloader_File_Not_Found: + //: ERROR ALL_PLATFORMS Download of the file failed with HTTP error code 404. return tr("File not found."); case Code::Downloader_Cannot_Save_File: + //: ERROR ALL_PLATFORMS Saving the downloaded file on the local disk failed. return tr("Cannot save file."); case Code::Downloader_Data_Corrupted: + //: ERROR ALL_PLATFORMS The downloaded file contained unsupported/invalid data. return tr("Received data were corrupted."); } Q_UNREACHABLE(); + return QString(); } @@ -365,6 +419,6 @@ bool GlobalStatus::isCancellationByUser() const QDebug operator <<(QDebug pDbg, const GlobalStatus& pStatus) { - pDbg << pStatus.getStatusCode() << "|" << pStatus.toErrorDescription(); + pDbg << pStatus.getStatusCode() << '|' << pStatus.toErrorDescription(); return pDbg; } diff --git a/src/global/GlobalStatus.h b/src/global/GlobalStatus.h index 2d17c41..943a318 100644 --- a/src/global/GlobalStatus.h +++ b/src/global/GlobalStatus.h @@ -40,7 +40,7 @@ class GlobalStatus Workflow_Cancellation_By_User, Workflow_Card_Removed, Workflow_Cannot_Confirm_IdCard_Authenticity, - Workflow_Unknown_Paos_Form_EidServer, + Workflow_Unknown_Paos_From_EidServer, Workflow_Unexpected_Message_From_EidServer, Workflow_Pin_Blocked_And_Puk_Objectionable, Workflow_Preverification_Developermode_Error, @@ -63,15 +63,13 @@ class GlobalStatus Workflow_TrustedChannel_ServiceUnavailable, Workflow_TrustedChannel_TimeOut, Workflow_TrustedChannel_Proxy_Error, - Workflow_TrustedChannel_Ssl_Establishment_Error, Workflow_TrustedChannel_Server_Format_Error, Workflow_TrustedChannel_Other_Network_Error, Workflow_Reader_Became_Inaccessible, - Workflow_Reader_Communication_Error, Workflow_Server_Incomplete_Information_Provided, Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length, Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length, - Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description, + Workflow_Network_Ssl_Hash_Not_In_Certificate_Description, Workflow_Network_Empty_Redirect_Url, Workflow_Network_Expected_Redirect, Workflow_Network_Invalid_Scheme, @@ -89,7 +87,7 @@ class GlobalStatus Paos_Error_SAL_Cancellation_by_User, Paos_Error_SAL_Invalid_Key, - Workflow_Reader_Device_Connection_Error, + Workflow_Bluetooth_Reader_Connection_Error, Workflow_Reader_Device_Scan_Error, Card_Not_Found, @@ -107,12 +105,9 @@ class GlobalStatus Card_NewPin_Mismatch, Card_NewPin_Invalid_Length, - RemoteReader_CloseCode_NormalClose, RemoteReader_CloseCode_AbnormalClose, - RemoteReader_CloseCode_Undefined, RemoteConnector_InvalidRequest, - RemoteConnector_EmptyPassword, RemoteConnector_NoSupportedApiLevel, RemoteConnector_ConnectionTimeout, RemoteConnector_ConnectionError, diff --git a/src/global/Initializer.h b/src/global/Initializer.h index 0209db8..7b14f19 100644 --- a/src/global/Initializer.h +++ b/src/global/Initializer.h @@ -29,7 +29,7 @@ class Initializer struct Entry final { - Entry(const std::function& pRegister) + explicit Entry(const std::function& pRegister) { Initializer::getInstance().add(pRegister); } diff --git a/src/global/LogCategories.cpp b/src/global/LogCategories.cpp index 373512b..9555845 100644 --- a/src/global/LogCategories.cpp +++ b/src/global/LogCategories.cpp @@ -19,7 +19,7 @@ Q_LOGGING_CATEGORY(paos, "paos") Q_LOGGING_CATEGORY(gui, "gui") Q_LOGGING_CATEGORY(language, "language") Q_LOGGING_CATEGORY(qml, "qml") -Q_LOGGING_CATEGORY(jsonapi, "jsonapi") +Q_LOGGING_CATEGORY(json, "json") Q_LOGGING_CATEGORY(aidl, "aidl") Q_LOGGING_CATEGORY(websocket, "websocket") Q_LOGGING_CATEGORY(cli, "cli") @@ -81,6 +81,11 @@ Q_LOGGING_CATEGORY(support, "support") Q_LOGGING_CATEGORY(developermode, "developermode") /*! - * Loggin category for persistent settings + * Logging category for persistent settings */ Q_LOGGING_CATEGORY(settings, "settings") + +/*! + * Logging category for ApplicationModel::showFeedback + */ +Q_LOGGING_CATEGORY(feedback, "feedback") diff --git a/src/global/LogHandler.cpp b/src/global/LogHandler.cpp index c3579dd..e69a04b 100644 --- a/src/global/LogHandler.cpp +++ b/src/global/LogHandler.cpp @@ -5,9 +5,14 @@ #include "LogHandler.h" #include "BreakPropertyBindingDiagnosticLogFilter.h" -#include "ScopeGuard.h" #include "SingletonHelper.h" +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include +#else +#include "ScopeGuard.h" +#endif + #include #include #include @@ -124,7 +129,7 @@ QByteArray LogHandler::readLogFile(qint64 pStart, qint64 pLength) if (mLogFile.isOpen() && mLogFile.isReadable()) { const auto currentPos = mLogFile.pos(); - const ScopeGuard resetPosition([this, currentPos] { + const auto resetPosition = qScopeGuard([this, currentPos] { mLogFile.seek(currentPos); }); @@ -134,6 +139,7 @@ QByteArray LogHandler::readLogFile(qint64 pStart, qint64 pLength) if (useLogfile()) { + //: LABEL ALL_PLATFORMS return tr("An error occurred in log handling: %1").arg(mLogFile.errorString()).toUtf8(); } @@ -220,7 +226,7 @@ void LogHandler::copyMessageLogContext(const QMessageLogContext& pSource, QMessa } -QByteArray LogHandler::formatFilename(const char* pFilename) const +QByteArray LogHandler::formatFilename(const char* const pFilename) const { QByteArray filename(pFilename); @@ -232,7 +238,7 @@ QByteArray LogHandler::formatFilename(const char* pFilename) const } -QByteArray LogHandler::formatFunction(const char* pFunction, const QByteArray& pFilename, int pLine) const +QByteArray LogHandler::formatFunction(const char* const pFunction, const QByteArray& pFilename, int pLine) const { QByteArray function(pFunction); diff --git a/src/global/LogHandler.h b/src/global/LogHandler.h index 4cc8f4d..d73bb74 100644 --- a/src/global/LogHandler.h +++ b/src/global/LogHandler.h @@ -57,8 +57,8 @@ class LogHandler inline void copyMessageLogContext(const QMessageLogContext& pSource, QMessageLogContext& pDestination, const QByteArray& pFilename = QByteArray(), const QByteArray& pFunction = QByteArray(), const QByteArray& pCategory = QByteArray()); inline void logToFile(const QString& pOutput); - inline QByteArray formatFunction(const char* pFunction, const QByteArray& pFilename, int pLine) const; - inline QByteArray formatFilename(const char* pFilename) const; + inline QByteArray formatFunction(const char* const pFunction, const QByteArray& pFilename, int pLine) const; + inline QByteArray formatFilename(const char* const pFilename) const; inline QByteArray formatCategory(const QByteArray& pCategory) const; QString getPaddedLogMsg(const QMessageLogContext& pContext, const QString& pMsg); diff --git a/src/global/ScopeGuard.h b/src/global/ScopeGuard.h index 8f2e68f..957c5ee 100644 --- a/src/global/ScopeGuard.h +++ b/src/global/ScopeGuard.h @@ -7,31 +7,48 @@ #pragma once #include +#include class test_ScopeGuard; namespace governikus { -class ScopeGuard +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + +class [[deprecated ("Use qScopeGuard")]] ScopeGuard { private: friend class ::test_ScopeGuard; + friend ScopeGuard qScopeGuard(const std::function &pFunc); const std::function mFunction; bool mEnabled; - public: - ScopeGuard(const ScopeGuard& pCopy) = delete; - ScopeGuard(const ScopeGuard&& pCopy) = delete; - ScopeGuard& operator=(const ScopeGuard& pCopy) = delete; - ScopeGuard& operator=(const ScopeGuard&& pCopy) = delete; - - ScopeGuard(const std::function& pFunc, bool pEnabled = true); - ~ScopeGuard(); + ScopeGuard(const std::function &pFunc, bool pEnabled = true); bool isEnabled() const; void setEnabled(bool pEnabled = true); + + public: + ScopeGuard(const ScopeGuard& pCopy) = delete; + ScopeGuard(const ScopeGuard && pCopy) = delete; + ScopeGuard& operator=(const ScopeGuard& pCopy) = delete; + ScopeGuard& operator=(const ScopeGuard&& pCopy) = delete; + + ~ScopeGuard(); }; +#if (QT_VERSION < QT_VERSION_CHECK(5, 12, 0)) +inline ScopeGuard qScopeGuard(const std::function& pFunc) +{ + return ScopeGuard(pFunc); +} + + +#endif + +QT_WARNING_POP + } // namespace governikus diff --git a/src/global/VersionInfo.h b/src/global/VersionInfo.h index b376100..46e139f 100644 --- a/src/global/VersionInfo.h +++ b/src/global/VersionInfo.h @@ -18,7 +18,7 @@ class VersionInfo { QMap mInfo; - VersionInfo(const QMap& pInfo); + explicit VersionInfo(const QMap& pInfo); public: VersionInfo(); diff --git a/src/global/VersionNumber.cpp b/src/global/VersionNumber.cpp index df70a4a..1edd5d5 100644 --- a/src/global/VersionNumber.cpp +++ b/src/global/VersionNumber.cpp @@ -47,7 +47,7 @@ int VersionNumber::getDistance() const { const int indexStart = mSuffix.indexOf(QLatin1Char('+')) + 1; const int indexEnd = mSuffix.indexOf(QLatin1Char('-'), indexStart); - if (indexStart && indexEnd) + if (indexStart != 0 && indexEnd != 0) { bool ok; int value = mSuffix.mid(indexStart, indexEnd - indexStart).toInt(&ok); @@ -65,7 +65,7 @@ QString VersionNumber::getBranch() const { const int indexStart = mSuffix.indexOf(QLatin1Char('-')) + 1; const int indexEnd = mSuffix.indexOf(QLatin1Char('-'), indexStart); - if (indexStart && indexEnd) + if (indexStart != 0 && indexEnd != 0) { return mSuffix.mid(indexStart, indexEnd - indexStart); } @@ -79,7 +79,7 @@ QString VersionNumber::getRevision() const if (mSuffix.count(QLatin1Char('-')) > 1) { const int index = mSuffix.lastIndexOf(QLatin1Char('-')) + 1; - if (index) + if (index != 0) { return mSuffix.mid(index); } diff --git a/src/global/VersionNumber.h b/src/global/VersionNumber.h index 630ef55..b8f50bc 100644 --- a/src/global/VersionNumber.h +++ b/src/global/VersionNumber.h @@ -21,7 +21,7 @@ class VersionNumber public: static const VersionNumber& getApplicationVersion(); - VersionNumber(const QString& pVersion); + explicit VersionNumber(const QString& pVersion); const QVersionNumber& getVersionNumber() const; bool isDeveloperVersion() const; diff --git a/src/main.cpp b/src/main.cpp index 837c30a..54c9c78 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include // version API #include +#include #include #include #include @@ -58,6 +59,10 @@ Q_IMPORT_PLUGIN(IntentActivationHandler) #if defined(Q_OS_IOS) +Q_IMPORT_PLUGIN(QIOSIntegrationPlugin) + +Q_IMPORT_PLUGIN(IosReaderManagerPlugIn) + Q_IMPORT_PLUGIN(CustomSchemeActivationHandler) Q_IMPORT_PLUGIN(QJpegPlugin) @@ -85,7 +90,6 @@ Q_IMPORT_PLUGIN(BluetoothReaderManagerPlugIn) #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_WINRT) -Q_IMPORT_PLUGIN(UIPlugInCli) Q_IMPORT_PLUGIN(UIPlugInWidgets) #endif @@ -93,11 +97,11 @@ Q_IMPORT_PLUGIN(UIPlugInWidgets) Q_IMPORT_PLUGIN(UIPlugInAidl) #endif -#if (defined(Q_OS_ANDROID) && !defined(ANDROID_BUILD_AAR)) || defined(Q_OS_IOS) || (!defined(QT_NO_DEBUG) && !defined(ANDROID_BUILD_AAR)) +#ifndef ANDROID_BUILD_AAR Q_IMPORT_PLUGIN(UIPlugInQml) #endif -Q_IMPORT_PLUGIN(UIPlugInJsonApi) +Q_IMPORT_PLUGIN(UIPlugInJson) Q_IMPORT_PLUGIN(UIPlugInWebSocket) @@ -141,6 +145,9 @@ static inline QCoreApplication* initQt(int& argc, char** argv) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); #endif +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif QCoreApplication::setOrganizationName(QStringLiteral(VENDOR)); QCoreApplication::setOrganizationDomain(QStringLiteral(VENDOR_DOMAIN)); @@ -151,6 +158,10 @@ static inline QCoreApplication* initQt(int& argc, char** argv) QGuiApplication::setQuitOnLastWindowClosed(false); #endif +#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) + QGuiApplication::setDesktopSettingsAware(false); +#endif + #if defined(Q_OS_ANDROID) && !defined(ANDROID_BUILD_AAR) if (QtAndroid::androidService().isValid()) { @@ -162,6 +173,31 @@ static inline QCoreApplication* initQt(int& argc, char** argv) } +#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) +void restartApp(const QString& pApplicationFilePath, QStringList pArgumentList, int pArgc) +{ + if (pArgumentList.size() == pArgc) + { + pArgumentList.removeFirst(); + } + + qCInfo(init) << "Attempting to start new process:" << pApplicationFilePath << ", args:" << pArgumentList; + qint64 pid = -1; + const bool restartSuccessful = QProcess::startDetached(pApplicationFilePath, pArgumentList, QString(), &pid); + + if (restartSuccessful) + { + qCInfo(init) << "New process successfully launched, PID:" << pid; + } + else + { + qCCritical(init) << "Could not launch new process."; + } +} + + +#endif + Q_DECL_EXPORT int main(int argc, char** argv) { const QScopedPointer app(initQt(argc, argv)); @@ -180,5 +216,19 @@ Q_DECL_EXPORT int main(int argc, char** argv) } SignalHandler::getInstance().setController(controller); - return SignalHandler::getInstance().shouldQuit() ? EXIT_SUCCESS : app->exec(); + if (SignalHandler::getInstance().shouldQuit()) + { + return EXIT_SUCCESS; + } + + const int returnCode = app->exec(); + +#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) + if (controller.shouldApplicationRestart()) + { + restartApp(app->applicationFilePath(), app->arguments(), argc); + } +#endif + + return returnCode; } diff --git a/src/network/DatagramHandler.h b/src/network/DatagramHandler.h index 472e6ba..d427f11 100644 --- a/src/network/DatagramHandler.h +++ b/src/network/DatagramHandler.h @@ -19,7 +19,7 @@ class DatagramHandler Q_OBJECT public: - DatagramHandler(bool pListen = true); + explicit DatagramHandler(bool pListen = true); virtual ~DatagramHandler(); virtual bool isBound() const = 0; virtual bool send(const QByteArray& pData) = 0; diff --git a/src/network/DatagramHandlerImpl.cpp b/src/network/DatagramHandlerImpl.cpp index 422979a..cb220ac 100644 --- a/src/network/DatagramHandlerImpl.cpp +++ b/src/network/DatagramHandlerImpl.cpp @@ -94,11 +94,11 @@ bool DatagramHandlerImpl::isBound() const bool DatagramHandlerImpl::send(const QByteArray& pData) { - return send(pData, 0); + return sendToAllAddressEntries(pData, 0); } -bool DatagramHandlerImpl::send(const QByteArray& pData, quint16 pPort) +bool DatagramHandlerImpl::sendToAllAddressEntries(const QByteArray& pData, quint16 pPort) { QVector broadcastAddresses; @@ -164,7 +164,7 @@ bool DatagramHandlerImpl::send(const QByteArray& pData, quint16 pPort) for (const QHostAddress& broadcastAddr : qAsConst(broadcastAddresses)) { - if (!send(pData, broadcastAddr, pPort)) + if (!sendToAddress(pData, broadcastAddr, pPort)) { qCDebug(network) << "Broadcasting to" << broadcastAddr << "failed"; return false; @@ -175,7 +175,7 @@ bool DatagramHandlerImpl::send(const QByteArray& pData, quint16 pPort) } -bool DatagramHandlerImpl::send(const QByteArray& pData, const QHostAddress& pAddress, quint16 pPort) +bool DatagramHandlerImpl::sendToAddress(const QByteArray& pData, const QHostAddress& pAddress, quint16 pPort) { // If port is 0 we should take our own listening port as destination as other instances // should use the same port to receive broadcasts. diff --git a/src/network/DatagramHandlerImpl.h b/src/network/DatagramHandlerImpl.h index 114cd75..dbed4f0 100644 --- a/src/network/DatagramHandlerImpl.h +++ b/src/network/DatagramHandlerImpl.h @@ -32,8 +32,8 @@ class DatagramHandlerImpl quint16 mUsedPort; PortFile mPortFile; - bool send(const QByteArray& pData, const QHostAddress& pAddress, quint16 pPort = 0); - bool send(const QByteArray& pData, quint16 pPort); + bool sendToAddress(const QByteArray& pData, const QHostAddress& pAddress, quint16 pPort = 0); + bool sendToAllAddressEntries(const QByteArray& pData, quint16 pPort); public: static quint16 cPort; diff --git a/src/network/HttpRequest.cpp b/src/network/HttpRequest.cpp index bd2f02f..e0fb292 100644 --- a/src/network/HttpRequest.cpp +++ b/src/network/HttpRequest.cpp @@ -145,7 +145,7 @@ void HttpRequest::onReadyRead() const auto errorCode = static_cast(mParser.http_errno); if (errorCode != HPE_OK) { - qCWarning(network) << "Http request not well-formed:" << http_errno_name(errorCode) << "|" << http_errno_description(errorCode); + qCWarning(network) << "Http request not well-formed:" << http_errno_name(errorCode) << '|' << http_errno_description(errorCode); } } @@ -184,7 +184,7 @@ int HttpRequest::onHeadersComplete(http_parser* pParser) } -int HttpRequest::onHeaderField(http_parser* pParser, const char* pPos, size_t pLength) +int HttpRequest::onHeaderField(http_parser* pParser, const char* const pPos, size_t pLength) { CAST_OBJ(pParser) obj->insertHeader(); @@ -193,7 +193,7 @@ int HttpRequest::onHeaderField(http_parser* pParser, const char* pPos, size_t pL } -int HttpRequest::onHeaderValue(http_parser* pParser, const char* pPos, size_t pLength) +int HttpRequest::onHeaderValue(http_parser* pParser, const char* const pPos, size_t pLength) { CAST_OBJ(pParser) add(obj->mCurrentHeaderValue, pPos, pLength); @@ -201,7 +201,7 @@ int HttpRequest::onHeaderValue(http_parser* pParser, const char* pPos, size_t pL } -int HttpRequest::onBody(http_parser* pParser, const char* pPos, size_t pLength) +int HttpRequest::onBody(http_parser* pParser, const char* const pPos, size_t pLength) { CAST_OBJ(pParser) add(obj->mBody, pPos, pLength); @@ -209,7 +209,7 @@ int HttpRequest::onBody(http_parser* pParser, const char* pPos, size_t pLength) } -int HttpRequest::onUrl(http_parser* pParser, const char* pPos, size_t pLength) +int HttpRequest::onUrl(http_parser* pParser, const char* const pPos, size_t pLength) { CAST_OBJ(pParser) add(obj->mUrl, pPos, pLength); diff --git a/src/network/HttpRequest.h b/src/network/HttpRequest.h index ee052a5..7556ed5 100644 --- a/src/network/HttpRequest.h +++ b/src/network/HttpRequest.h @@ -35,12 +35,12 @@ class HttpRequest static int onMessageBegin(http_parser* pParser); static int onMessageComplete(http_parser* pParser); static int onHeadersComplete(http_parser* pParser); - static int onHeaderField(http_parser* pParser, const char* pPos, size_t pLength); - static int onHeaderValue(http_parser* pParser, const char* pPos, size_t pLength); - static int onBody(http_parser* pParser, const char* pPos, size_t pLength); - static int onUrl(http_parser* pParser, const char* pPos, size_t pLength); + static int onHeaderField(http_parser* pParser, const char* const pPos, size_t pLength); + static int onHeaderValue(http_parser* pParser, const char* const pPos, size_t pLength); + static int onBody(http_parser* pParser, const char* const pPos, size_t pLength); + static int onUrl(http_parser* pParser, const char* const pPos, size_t pLength); - static inline void add(QByteArray& pDest, const char* pPos, size_t pLength) + static inline void add(QByteArray& pDest, const char* const pPos, size_t pLength) { pDest += QByteArray(pPos, static_cast(pLength)); } diff --git a/src/network/HttpServer.h b/src/network/HttpServer.h index 161d1dc..a362f00 100644 --- a/src/network/HttpServer.h +++ b/src/network/HttpServer.h @@ -31,7 +31,7 @@ class HttpServer public: static quint16 cPort; - HttpServer(quint16 pPort = HttpServer::cPort); + explicit HttpServer(quint16 pPort = HttpServer::cPort); virtual ~HttpServer(); bool isListening() const; diff --git a/src/network/MulticastLock.cpp b/src/network/MulticastLock.cpp index 0308132..765dfee 100644 --- a/src/network/MulticastLock.cpp +++ b/src/network/MulticastLock.cpp @@ -34,7 +34,7 @@ MulticastLock::~MulticastLock() } -void MulticastLock::invokeJniMethod(const char* pMethodName) +void MulticastLock::invokeJniMethod(const char* const pMethodName) { #if defined(Q_OS_ANDROID) QAndroidJniEnvironment env; @@ -57,6 +57,6 @@ void MulticastLock::invokeJniMethod(const char* pMethodName) env->ExceptionClear(); } #else - Q_UNUSED(pMethodName); + Q_UNUSED(pMethodName) #endif } diff --git a/src/network/MulticastLock.h b/src/network/MulticastLock.h index 909845f..92cc984 100644 --- a/src/network/MulticastLock.h +++ b/src/network/MulticastLock.h @@ -10,7 +10,7 @@ namespace governikus class MulticastLock { private: - void invokeJniMethod(const char* pMethodName); + void invokeJniMethod(const char* const pMethodName); public: MulticastLock(); diff --git a/src/network/NetworkManager.cpp b/src/network/NetworkManager.cpp index ab63db3..decc0f5 100644 --- a/src/network/NetworkManager.cpp +++ b/src/network/NetworkManager.cpp @@ -85,7 +85,7 @@ QNetworkReply* NetworkManager::paos(QNetworkRequest& pRequest, QNetworkReply* response; SecureStorage::TlsSuite tlsSuite = pUsePsk ? SecureStorage::TlsSuite::PSK : SecureStorage::TlsSuite::DEFAULT; - auto cfg = SecureStorage::getInstance().getTlsConfig(tlsSuite).getConfiguration(); + auto cfg = Env::getSingleton()->getTlsConfig(tlsSuite).getConfiguration(); cfg.setSessionTicket(pSslSession); pRequest.setSslConfiguration(cfg); response = mNetAccessManager->post(pRequest, pData); @@ -105,7 +105,7 @@ QNetworkReply* NetworkManager::get(QNetworkRequest& pRequest, } pRequest.setHeader(QNetworkRequest::UserAgentHeader, getUserAgentHeader()); - auto cfg = SecureStorage::getInstance().getTlsConfig().getConfiguration(); + auto cfg = Env::getSingleton()->getTlsConfig().getConfiguration(); cfg.setSessionTicket(pSslSession); pRequest.setSslConfiguration(cfg); QNetworkReply* response = mNetAccessManager->get(pRequest); @@ -126,7 +126,7 @@ QNetworkReply* NetworkManager::post(QNetworkRequest& pRequest, pRequest.setHeader(QNetworkRequest::UserAgentHeader, getUserAgentHeader()); pRequest.setHeader(QNetworkRequest::ContentLengthHeader, QString::number(pData.size())); - auto cfg = SecureStorage::getInstance().getTlsConfig(SecureStorage::TlsSuite::DEFAULT).getConfiguration(); + auto cfg = Env::getSingleton()->getTlsConfig(SecureStorage::TlsSuite::DEFAULT).getConfiguration(); pRequest.setSslConfiguration(cfg); QNetworkReply* response = mNetAccessManager->post(pRequest, pData); @@ -137,7 +137,7 @@ QNetworkReply* NetworkManager::post(QNetworkRequest& pRequest, bool NetworkManager::checkUpdateServerCertificate(const QNetworkReply& pReply) { - const QVector& trustedCertificates = SecureStorage::getInstance().getUpdateCertificates(); + const QVector& trustedCertificates = Env::getSingleton()->getUpdateCertificates(); const auto& cert = pReply.sslConfiguration().peerCertificate(); return !cert.isNull() && trustedCertificates.contains(cert); @@ -179,7 +179,7 @@ NetworkManager::NetworkError NetworkManager::toNetworkError(const QNetworkReply* return NetworkError::ProxyError; case QNetworkReply::SslHandshakeFailedError: - return NetworkError::SslError; + return NetworkError::SecurityError; default: qCCritical(network) << "Network error opening URL" << pNetworkReply->request().url().toString(); @@ -202,8 +202,8 @@ GlobalStatus NetworkManager::toTrustedChannelStatus(const QNetworkReply* const p case NetworkManager::NetworkError::ProxyError: return GlobalStatus::Code::Workflow_TrustedChannel_Proxy_Error; - case NetworkManager::NetworkError::SslError: - return GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error; + case NetworkManager::NetworkError::SecurityError: + return GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error; case NetworkManager::NetworkError::OtherError: return GlobalStatus::Code::Workflow_TrustedChannel_Other_Network_Error; @@ -226,7 +226,7 @@ GlobalStatus NetworkManager::toStatus(const QNetworkReply* const pNetworkReply) case NetworkManager::NetworkError::ProxyError: return GlobalStatus::Code::Network_Proxy_Error; - case NetworkManager::NetworkError::SslError: + case NetworkManager::NetworkError::SecurityError: return GlobalStatus::Code::Network_Ssl_Establishment_Error; case NetworkManager::NetworkError::OtherError: diff --git a/src/network/NetworkManager.h b/src/network/NetworkManager.h index 804fae0..17b8631 100644 --- a/src/network/NetworkManager.h +++ b/src/network/NetworkManager.h @@ -55,7 +55,7 @@ class NetworkManager ServiceUnavailable, TimeOut, ProxyError, - SslError, + SecurityError, OtherError, }; Q_ENUM(NetworkError) @@ -85,7 +85,7 @@ class NetworkManager const QByteArray& pSslSession = QByteArray(), int pTimeoutInMilliSeconds = 30000); - QNetworkReply* post(QNetworkRequest& pRequest, + virtual QNetworkReply* post(QNetworkRequest& pRequest, const QByteArray& pData, int pTimeoutInMilliSeconds = 30000); diff --git a/src/network/NetworkReplyError.cpp b/src/network/NetworkReplyError.cpp index 2019356..5083150 100644 --- a/src/network/NetworkReplyError.cpp +++ b/src/network/NetworkReplyError.cpp @@ -9,8 +9,8 @@ using namespace governikus; qint64 NetworkReplyError::readData(char* data, qint64 maxlen) { - Q_UNUSED(data); - Q_UNUSED(maxlen); + Q_UNUSED(data) + Q_UNUSED(maxlen) return -1; } diff --git a/src/network/NetworkReplyTimeout.cpp b/src/network/NetworkReplyTimeout.cpp index b3ea577..8c32a93 100644 --- a/src/network/NetworkReplyTimeout.cpp +++ b/src/network/NetworkReplyTimeout.cpp @@ -14,7 +14,7 @@ NetworkReplyTimeout::NetworkReplyTimeout(QNetworkReply* pReply, const int pTimeo : QObject(pReply) { Q_ASSERT(pReply); - if (!pReply) + if (pReply == nullptr) { return; } @@ -28,7 +28,7 @@ NetworkReplyTimeout::NetworkReplyTimeout(QNetworkReply* pReply, const int pTimeo void NetworkReplyTimeout::onTimeout() { - QNetworkReply* reply = static_cast(parent()); + auto* reply = static_cast(parent()); if (reply != nullptr && reply->isRunning()) { reply->abort(); @@ -46,6 +46,6 @@ void NetworkReplyTimeout::onShutdown() void NetworkReplyTimeout::setTimeout(QNetworkReply* pReply, const int pTimeout) { // since the QNetworkReply is set as parent, we don't need to care about destruction - NetworkReplyTimeout* timeout = new NetworkReplyTimeout(pReply, pTimeout); + auto* timeout = new NetworkReplyTimeout(pReply, pTimeout); connect(Env::getSingleton(), &NetworkManager::fireShutdown, timeout, &NetworkReplyTimeout::onShutdown); } diff --git a/src/network/TlsChecker.cpp b/src/network/TlsChecker.cpp index 6a3361c..9e28308 100644 --- a/src/network/TlsChecker.cpp +++ b/src/network/TlsChecker.cpp @@ -10,11 +10,13 @@ #include #include -#include -#include -#include -#include -#include +#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0)) + #include + #include + #include + #include + #include +#endif Q_DECLARE_LOGGING_CATEGORY(developermode) Q_DECLARE_LOGGING_CATEGORY(network) @@ -64,6 +66,9 @@ bool TlsChecker::hasValidEphemeralKeyLength(const QSslKey& pEphemeralServerKey) if (keyAlgorithm == QSsl::Opaque) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + qWarning() << "Qt failed to determine algorithm"; +#else qWarning() << "Qt failed to determine algorithm... fallback to internal handling"; // try do determine key algorithm and length if (auto key = static_cast(pEphemeralServerKey.handle())) @@ -86,6 +91,7 @@ bool TlsChecker::hasValidEphemeralKeyLength(const QSslKey& pEphemeralServerKey) break; } } +#endif } qDebug() << "Check ephemeral key of type" << TlsChecker::toString(keyAlgorithm) << "and key size" << keyLength; @@ -106,8 +112,8 @@ QString TlsChecker::getCertificateIssuerName(const QSslCertificate& pCertificate bool TlsChecker::isValidKeyLength(int pKeyLength, QSsl::KeyAlgorithm pKeyAlgorithm, bool pIsEphemeral) { - const auto& secureStorage = SecureStorage::getInstance(); - const int minKeySize = pIsEphemeral ? secureStorage.getMinimumEphemeralKeySize(pKeyAlgorithm) : secureStorage.getMinimumStaticKeySize(pKeyAlgorithm); + const auto* secureStorage = Env::getSingleton(); + const int minKeySize = pIsEphemeral ? secureStorage->getMinimumEphemeralKeySize(pKeyAlgorithm) : secureStorage->getMinimumStaticKeySize(pKeyAlgorithm); qDebug() << "Minimum requested key size" << minKeySize; @@ -191,6 +197,7 @@ QString TlsChecker::toString(QSsl::SslProtocol pProtocol) } Q_UNREACHABLE(); + return QString(); } @@ -284,7 +291,7 @@ bool TlsChecker::containsFatalError(QNetworkReply* pReply, const QList& pEntry); + void fireDeviceUpdated(const QSharedPointer& pEntry); void fireDeviceVanished(const QSharedPointer& pEntry); - void fireEstablishConnectionDone(const QSharedPointer& pEntry, GlobalStatus pStatus); + void fireEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus); void fireNewRemoteDispatcher(const QSharedPointer& pRemoteDispatcher); void fireRemoteDevicesInfo(const QVector >& pRemoteDevices); void fireDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QString& pId); void fireDetectionChanged(); - void fireCertificateRemoved(QString pDeviceName); + void fireCertificateRemoved(const QString& pDeviceName); public: RemoteClient() = default; diff --git a/src/remote_device/RemoteClientImpl.cpp b/src/remote_device/RemoteClientImpl.cpp index 0e12b03..5b14b3e 100644 --- a/src/remote_device/RemoteClientImpl.cpp +++ b/src/remote_device/RemoteClientImpl.cpp @@ -28,6 +28,7 @@ RemoteClientImpl::RemoteClientImpl() , mRemoteConnectorPending() { connect(mRemoteDeviceList.data(), &RemoteDeviceList::fireDeviceAppeared, this, &RemoteClient::fireDeviceAppeared); + connect(mRemoteDeviceList.data(), &RemoteDeviceList::fireDeviceUpdated, this, &RemoteClient::fireDeviceUpdated); connect(mRemoteDeviceList.data(), &RemoteDeviceList::fireDeviceVanished, this, &RemoteClient::fireDeviceVanished); bootstrapRemoteConnectorThread(); diff --git a/src/remote_device/RemoteConnector.cpp b/src/remote_device/RemoteConnector.cpp index 3d940f3..57d631b 100644 --- a/src/remote_device/RemoteConnector.cpp +++ b/src/remote_device/RemoteConnector.cpp @@ -31,9 +31,6 @@ GlobalStatus RemoteConnector::errorToGlobalStatus(RemoteErrorCode pDeviceError) case RemoteErrorCode::INVALID_REQUEST: return GlobalStatus::Code::RemoteConnector_InvalidRequest; - case RemoteErrorCode::EMPTY_PASSWORD: - return GlobalStatus::Code::RemoteConnector_EmptyPassword; - case RemoteErrorCode::NO_SUPPORTED_API_LEVEL: return GlobalStatus::Code::RemoteConnector_NoSupportedApiLevel; diff --git a/src/remote_device/RemoteConnector.h b/src/remote_device/RemoteConnector.h index 462a75d..5cddc40 100644 --- a/src/remote_device/RemoteConnector.h +++ b/src/remote_device/RemoteConnector.h @@ -17,7 +17,6 @@ namespace governikus { defineEnumType(RemoteErrorCode, INVALID_REQUEST, - EMPTY_PASSWORD, NO_SUPPORTED_API_LEVEL, CONNECTION_TIMEOUT, CONNECTION_ERROR, diff --git a/src/remote_device/RemoteConnectorImpl.cpp b/src/remote_device/RemoteConnectorImpl.cpp index 5673315..f74b870 100644 --- a/src/remote_device/RemoteConnectorImpl.cpp +++ b/src/remote_device/RemoteConnectorImpl.cpp @@ -88,7 +88,7 @@ void ConnectRequest::onConnected() qCDebug(remote_device) << "Connected to remote device"; auto& settings = Env::getSingleton()->getRemoteServiceSettings(); - const auto& pairingCiphers = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); + const auto& pairingCiphers = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); if (pairingCiphers.contains(cfg.sessionCipher())) { qCDebug(remote_device) << "Pairing completed | Add certificate:" << cfg.peerCertificate(); @@ -142,7 +142,7 @@ void ConnectRequest::onSslErrors(const QList& pErrors) }; const auto& config = mSocket->sslConfiguration(); - const auto& pairingCiphers = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); + const auto& pairingCiphers = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); if (pairingCiphers.contains(config.sessionCipher())) { allowedErrors << QSslError::SelfSignedCertificate; @@ -187,13 +187,13 @@ ConnectRequest::ConnectRequest(const RemoteDeviceDescriptor& pRemoteDeviceDescri QSslConfiguration config; if (mPsk.isEmpty()) { - config = SecureStorage::getInstance().getTlsConfigRemote().getConfiguration(); + config = Env::getSingleton()->getTlsConfigRemote().getConfiguration(); config.setCaCertificates(remoteServiceSettings.getTrustedCertificates()); qCCritical(remote_device) << "Start reconnect to server"; } else { - config = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); + config = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); qCCritical(remote_device) << "Start pairing to server"; } config.setPrivateKey(remoteServiceSettings.getKey()); diff --git a/src/remote_device/RemoteDeviceList.cpp b/src/remote_device/RemoteDeviceList.cpp index 583034a..d0cfbf4 100644 --- a/src/remote_device/RemoteDeviceList.cpp +++ b/src/remote_device/RemoteDeviceList.cpp @@ -7,6 +7,8 @@ #include "Env.h" #include "Initializer.h" +#include + using namespace governikus; @@ -37,16 +39,54 @@ template<> RemoteDeviceList* createNewObject(int&& RemoteDeviceListEntry::RemoteDeviceListEntry(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor) : mRemoteDeviceDescriptor(pRemoteDeviceDescriptor) , mLastSeen(QTime::currentTime()) + , mLastSeenHistory() { } void RemoteDeviceListEntry::setLastSeenToNow() { + if (mLastSeen.isValid()) + { + mLastSeenHistory += mLastSeen; + } + mLastSeen = QTime::currentTime(); } +bool RemoteDeviceListEntry::cleanUpSeenTimestamps(int pReaderResponsiveTimeout) +{ + bool entryRemoved = false; + const int visibilityOld = getPercentSeen(); + + const QTime threshold(QTime::currentTime().addMSecs(-pReaderResponsiveTimeout)); + QMutableVectorIterator i(mLastSeenHistory); + while (i.hasNext()) + { + if (i.next() < threshold) + { + i.remove(); + entryRemoved = true; + } + } + + return entryRemoved && getPercentSeen() != visibilityOld; +} + + +int RemoteDeviceListEntry::getPercentSeen(int pCheckInterval, int pTimeFrame) const +{ + const int count = mLastSeenHistory.size(); + const int expectedMax = pTimeFrame / pCheckInterval; + const int percent = 100 * count / expectedMax; + + // Maximum is calculated based on the assumption, that only IPv4 is in use. + // If IPv6 is used in parallel - even better. + return qMin(percent, 100); +} + + bool RemoteDeviceListEntry::containsEquivalent(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor) const { return mRemoteDeviceDescriptor.isEquivalent(pRemoteDeviceDescriptor); @@ -96,6 +136,7 @@ RemoteDeviceListImpl::RemoteDeviceListImpl(int pCheckInterval, int pReaderRespon , mResponsiveList() { connect(&mTimer, &QTimer::timeout, this, &RemoteDeviceListImpl::onProcessUnresponsiveRemoteReaders); + pCheckInterval = pCheckInterval / 2 - 1; // Nyquist-Shannon sampling theorem. Enable smooth UI updates. mTimer.setInterval(pCheckInterval); } @@ -112,7 +153,12 @@ void RemoteDeviceListImpl::update(const RemoteDeviceDescriptor& pDescriptor) { if (entry->containsEquivalent(pDescriptor)) { + const int visibilityOld = entry->getPercentSeen(); entry->setLastSeenToNow(); + if (entry->getPercentSeen() != visibilityOld) + { + Q_EMIT fireDeviceUpdated(entry); + } return; } } @@ -152,6 +198,12 @@ void RemoteDeviceListImpl::onProcessUnresponsiveRemoteReaders() { i.remove(); Q_EMIT fireDeviceVanished(entry); + continue; + } + + if (entry->cleanUpSeenTimestamps(mReaderResponsiveTimeout)) + { + Q_EMIT fireDeviceUpdated(entry); } } diff --git a/src/remote_device/RemoteDeviceList.h b/src/remote_device/RemoteDeviceList.h index 735d258..80b8c62 100644 --- a/src/remote_device/RemoteDeviceList.h +++ b/src/remote_device/RemoteDeviceList.h @@ -22,11 +22,14 @@ class RemoteDeviceListEntry private: const RemoteDeviceDescriptor mRemoteDeviceDescriptor; QTime mLastSeen; + QVector mLastSeenHistory; public: RemoteDeviceListEntry(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor); void setLastSeenToNow(); + bool cleanUpSeenTimestamps(int pReaderResponsiveTimeout); + int getPercentSeen(int pCheckInterval = 1000, int pTimeFrame = 5000) const; bool containsEquivalent(const RemoteDeviceDescriptor& pRemoteDeviceDescriptor) const; bool isEqual(const RemoteDeviceListEntry* const pOther) const; @@ -44,6 +47,7 @@ class RemoteDeviceList Q_SIGNALS: void fireDeviceAppeared(const QSharedPointer&); + void fireDeviceUpdated(const QSharedPointer&); void fireDeviceVanished(const QSharedPointer&); public: diff --git a/src/remote_device/RemoteDispatcher.cpp b/src/remote_device/RemoteDispatcher.cpp index f338f1c..b7114cb 100644 --- a/src/remote_device/RemoteDispatcher.cpp +++ b/src/remote_device/RemoteDispatcher.cpp @@ -50,7 +50,7 @@ void RemoteDispatcher::onReceived(const QByteArray& pDataBlock) if (messageType == RemoteCardMessageType::UNDEFINED) { - const auto& errorMessage = QSharedPointer::create(QString(), ECardApiResult::Minor::AL_Unkown_API_Function); + const QSharedPointer& errorMessage = QSharedPointer::create(QString(), ECardApiResult::Minor::AL_Unkown_API_Function); send(errorMessage); return; } diff --git a/src/remote_device/RemoteDispatcherClient.cpp b/src/remote_device/RemoteDispatcherClient.cpp index 517cf50..265275b 100644 --- a/src/remote_device/RemoteDispatcherClient.cpp +++ b/src/remote_device/RemoteDispatcherClient.cpp @@ -67,6 +67,6 @@ void RemoteDispatcherClient::sendEstablishContext() qCDebug(remote_device) << "Try to establish context with version" << mVersion; RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); - const auto& establishContext = QSharedPointer::create(mVersion, settings.getServerName()); + const QSharedPointer& establishContext = QSharedPointer::create(mVersion, settings.getServerName()); send(establishContext); } diff --git a/src/remote_device/RemoteTlsServer.cpp b/src/remote_device/RemoteTlsServer.cpp index de29e7a..3b54abd 100644 --- a/src/remote_device/RemoteTlsServer.cpp +++ b/src/remote_device/RemoteTlsServer.cpp @@ -68,7 +68,7 @@ void RemoteTlsServer::incomingConnection(qintptr pSocketDescriptor) mSocket = new QSslSocket(); const auto cipherCfg = mPsk.isEmpty() ? SecureStorage::TlsSuite::DEFAULT : SecureStorage::TlsSuite::PSK; - QSslConfiguration config = SecureStorage::getInstance().getTlsConfigRemote(cipherCfg).getConfiguration(); + QSslConfiguration config = Env::getSingleton()->getTlsConfigRemote(cipherCfg).getConfiguration(); const auto& settings = Env::getSingleton()->getRemoteServiceSettings(); config.setPrivateKey(settings.getKey()); config.setLocalCertificate(settings.getCertificate()); @@ -83,15 +83,11 @@ void RemoteTlsServer::incomingConnection(qintptr pSocketDescriptor) { connect(mSocket.data(), QOverload&>::of(&QSslSocket::sslErrors), this, &RemoteTlsServer::onSslErrors); - connect(mSocket.data(), QOverload::of(&QAbstractSocket::error), this, &RemoteTlsServer::onError); - connect(mSocket.data(), &QSslSocket::preSharedKeyAuthenticationRequired, this, &RemoteTlsServer::onPreSharedKeyAuthenticationRequired); - - connect(mSocket.data(), &QSslSocket::encrypted, - this, &RemoteTlsServer::onEncrypted); + connect(mSocket.data(), &QSslSocket::encrypted, this, &RemoteTlsServer::onEncrypted); mSocket->startServerEncryption(); } @@ -128,7 +124,7 @@ void RemoteTlsServer::onSslErrors(const QList& pErrors) { if (pErrors.size() == 1 && pErrors.first().error() == QSslError::SelfSignedCertificate) { - const auto& pairingCiphers = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); + const auto& pairingCiphers = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); if (pairingCiphers.contains(mSocket->sessionCipher())) { qCDebug(remote_device) << "Client requests pairing | cipher:" << mSocket->sessionCipher() << "| certificate:" << mSocket->peerCertificate(); @@ -157,7 +153,7 @@ void RemoteTlsServer::onEncrypted() qCDebug(remote_device) << "Client connected"; auto& settings = Env::getSingleton()->getRemoteServiceSettings(); - const auto& pairingCiphers = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); + const auto& pairingCiphers = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); if (pairingCiphers.contains(cfg.sessionCipher())) { qCDebug(remote_device) << "Pairing completed | Add certificate:" << cfg.peerCertificate(); diff --git a/src/remote_device/ServerMessageHandler.cpp b/src/remote_device/ServerMessageHandler.cpp index 36deec8..91ca40b 100644 --- a/src/remote_device/ServerMessageHandler.cpp +++ b/src/remote_device/ServerMessageHandler.cpp @@ -147,6 +147,8 @@ void ServerMessageHandlerImpl::onCreateCardConnectionCommandDone(QSharedPointer< const auto& response = QSharedPointer::create(slotHandle); mRemoteDispatcher->send(response); + + Q_EMIT fireCardConnectionEstablished(pCommand->getCardConnection()); } @@ -384,7 +386,7 @@ void ServerMessageHandlerImpl::onClosed() } -void ServerMessageHandlerImpl::onRemoteMessage(RemoteCardMessageType pMessageType, const QJsonObject pJsonObject) +void ServerMessageHandlerImpl::onRemoteMessage(RemoteCardMessageType pMessageType, const QJsonObject& pJsonObject) { switch (pMessageType) { @@ -404,7 +406,7 @@ void ServerMessageHandlerImpl::onRemoteMessage(RemoteCardMessageType pMessageTyp qCWarning(remote_device) << "Received an unexpected message of type:" << pMessageType; const auto& localCopy = mRemoteDispatcher; QMetaObject::invokeMethod(localCopy.data(), [localCopy] { - const auto& errorMessage = QSharedPointer::create(QString(), ECardApiResult::Minor::AL_Unkown_API_Function); + const QSharedPointer& errorMessage = QSharedPointer::create(QString(), ECardApiResult::Minor::AL_Unkown_API_Function); localCopy->send(errorMessage); }, Qt::QueuedConnection); break; diff --git a/src/remote_device/ServerMessageHandler.h b/src/remote_device/ServerMessageHandler.h index e3e4dd3..788145b 100644 --- a/src/remote_device/ServerMessageHandler.h +++ b/src/remote_device/ServerMessageHandler.h @@ -37,6 +37,7 @@ class ServerMessageHandler virtual void sendModifyPinResponse(const QString& pSlotHandle, const ResponseApdu& pResponseApdu) = 0; Q_SIGNALS: + void fireCardConnectionEstablished(const QSharedPointer& pConnection); void fireEstablishPaceChannel(const QSharedPointer& pMessage, const QSharedPointer& pConnection); void fireModifyPin(const QSharedPointer& pMessage, const QSharedPointer& pConnection); void fireClosed(); @@ -68,7 +69,7 @@ class ServerMessageHandlerImpl void onCreateCardConnectionCommandDone(QSharedPointer pCommand); void onTransmitCardCommandDone(QSharedPointer pCommand); void onClosed(); - void onRemoteMessage(RemoteCardMessageType pMessageType, const QJsonObject pJsonObject); + void onRemoteMessage(RemoteCardMessageType pMessageType, const QJsonObject& pJsonObject); void onReaderChanged(const QString& pReaderName); void onReaderRemoved(const QString& pReaderName); diff --git a/src/remote_device/WebSocketChannel.cpp b/src/remote_device/WebSocketChannel.cpp index 80bd801..bcf213d 100644 --- a/src/remote_device/WebSocketChannel.cpp +++ b/src/remote_device/WebSocketChannel.cpp @@ -115,7 +115,7 @@ void WebSocketChannel::onDisconnected() if (mConnection) { Q_EMIT fireClosed(mConnection->closeCode() == QWebSocketProtocol::CloseCodeNormal ? - GlobalStatus::Code::RemoteReader_CloseCode_NormalClose : + GlobalStatus::Code::No_Error : GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose); } } diff --git a/src/remote_device/messages/IfdStatus.h b/src/remote_device/messages/IfdStatus.h index 47382cd..a066912 100644 --- a/src/remote_device/messages/IfdStatus.h +++ b/src/remote_device/messages/IfdStatus.h @@ -47,8 +47,6 @@ class IfdStatus { private: friend MockRemoteDispatcher; - friend ::test_RemoteReaderManagerPlugIn; - friend ::test_ServerMessageHandler; friend ::test_IfdStatus; QString mSlotName; diff --git a/src/remote_device/messages/IfdTransmit.cpp b/src/remote_device/messages/IfdTransmit.cpp index 2703c6a..74b743a 100644 --- a/src/remote_device/messages/IfdTransmit.cpp +++ b/src/remote_device/messages/IfdTransmit.cpp @@ -25,7 +25,7 @@ VALUE_NAME(ACCEPTABLE_STATUS_CODES, "AcceptableStatusCodes") } // namespace -void IfdTransmit::parseCommandApdu(QJsonValue pEntry) +void IfdTransmit::parseCommandApdu(const QJsonValue& pEntry) { if (!pEntry.isObject()) { diff --git a/src/remote_device/messages/IfdTransmit.h b/src/remote_device/messages/IfdTransmit.h index f32d56c..e6192c6 100644 --- a/src/remote_device/messages/IfdTransmit.h +++ b/src/remote_device/messages/IfdTransmit.h @@ -18,7 +18,7 @@ class IfdTransmit QString mSlotHandle; QByteArray mInputApdu; - void parseCommandApdu(QJsonValue pEntry); + void parseCommandApdu(const QJsonValue& pEntry); public: IfdTransmit(const QString& pSlotHandle, const QByteArray& pInputApdu); diff --git a/src/remote_device/messages/IfdVersion.cpp b/src/remote_device/messages/IfdVersion.cpp index 3107f59..7f9dd75 100644 --- a/src/remote_device/messages/IfdVersion.cpp +++ b/src/remote_device/messages/IfdVersion.cpp @@ -51,6 +51,7 @@ QString IfdVersion::toString() const } Q_UNREACHABLE(); + return QString(); } diff --git a/src/remote_device/plugin/RemoteCard.cpp b/src/remote_device/plugin/RemoteCard.cpp index da6e250..b7ecf6e 100644 --- a/src/remote_device/plugin/RemoteCard.cpp +++ b/src/remote_device/plugin/RemoteCard.cpp @@ -59,7 +59,7 @@ bool RemoteCard::sendMessage(const QSharedPointer& pMessage } -void RemoteCard::onMessageReceived(RemoteCardMessageType pMessageTpe, const QJsonObject pJsonObject) +void RemoteCard::onMessageReceived(RemoteCardMessageType pMessageTpe, const QJsonObject& pJsonObject) { QMutexLocker locker(&mProcessResponse); @@ -79,7 +79,7 @@ void RemoteCard::onMessageReceived(RemoteCardMessageType pMessageTpe, const QJso void RemoteCard::onDispatcherClosed(GlobalStatus::Code pCloseCode, const QString& pId) { - Q_UNUSED(pId); + Q_UNUSED(pId) QMutexLocker locker(&mProcessResponse); if (mWaitingForAnswer) @@ -174,7 +174,7 @@ bool RemoteCard::isConnected() CardReturnCode RemoteCard::transmit(const CommandApdu& pCommand, ResponseApdu& pResponse) { - const auto& transmitCmd = QSharedPointer::create(mSlotHandle, pCommand.getBuffer()); + const QSharedPointer& transmitCmd = QSharedPointer::create(mSlotHandle, pCommand.getBuffer()); if (sendMessage(transmitCmd, RemoteCardMessageType::IFDTransmitResponse, 5000)) { const IfdTransmitResponse response(mResponse); @@ -195,7 +195,7 @@ CardReturnCode RemoteCard::transmit(const CommandApdu& pCommand, ResponseApdu& p } -CardReturnCode RemoteCard::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds) +EstablishPaceChannelOutput RemoteCard::establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, quint8 pTimeoutSeconds) { EstablishPaceChannel builder; builder.setPasswordId(pPasswordId); @@ -203,7 +203,7 @@ CardReturnCode RemoteCard::establishPaceChannel(PacePasswordId pPasswordId, cons builder.setCertificateDescription(pCertificateDescription); const QByteArray inputData = builder.createCommandDataCcid().getBuffer(); - const auto& message = QSharedPointer::create(mSlotHandle, inputData); + const QSharedPointer& message = QSharedPointer::create(mSlotHandle, inputData); if (sendMessage(message, RemoteCardMessageType::IFDEstablishPACEChannelResponse, pTimeoutSeconds * 1000)) { const IfdEstablishPaceChannelResponse response(mResponse); @@ -216,8 +216,9 @@ CardReturnCode RemoteCard::establishPaceChannel(PacePasswordId pPasswordId, cons if (!response.resultHasError()) { - pChannelOutput.parseFromCcid(response.getOutputData(), pPasswordId); - return pChannelOutput.getPaceReturnCode(); + EstablishPaceChannelOutput output; + output.parseFromCcid(response.getOutputData(), pPasswordId); + return output; } qCWarning(card_remote) << response.getResultMinor(); } @@ -234,7 +235,7 @@ CardReturnCode RemoteCard::setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResp PinModify pinModify(pTimeoutSeconds); const QByteArray inputData = pinModify.createCcidForRemote(); - const auto& message = QSharedPointer::create(mSlotHandle, inputData); + const QSharedPointer& message = QSharedPointer::create(mSlotHandle, inputData); if (sendMessage(message, RemoteCardMessageType::IFDModifyPINResponse, pTimeoutSeconds * 1000)) { const IfdModifyPinResponse response(mResponse); @@ -245,7 +246,7 @@ CardReturnCode RemoteCard::setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResp if (!response.isIncomplete()) { - PinModifyOutput output(response.getOutputData()); + PinModifyOutput output(ResponseApdu(response.getOutputData())); pResponseApdu.setBuffer(output.getResponseApdu().getBuffer()); if (!response.resultHasError()) { diff --git a/src/remote_device/plugin/RemoteCard.h b/src/remote_device/plugin/RemoteCard.h index 9f36c2f..b65f33a 100644 --- a/src/remote_device/plugin/RemoteCard.h +++ b/src/remote_device/plugin/RemoteCard.h @@ -39,7 +39,7 @@ class RemoteCard bool sendMessage(const QSharedPointer& pMessage, RemoteCardMessageType pExpectedAnswer, unsigned long pTimeout); private Q_SLOTS: - void onMessageReceived(RemoteCardMessageType pMessageTpe, const QJsonObject pJsonObject); + void onMessageReceived(RemoteCardMessageType pMessageTpe, const QJsonObject& pJsonObject); void onDispatcherClosed(GlobalStatus::Code pCloseCode, const QString& pId); Q_SIGNALS: @@ -55,7 +55,7 @@ class RemoteCard virtual CardReturnCode transmit(const CommandApdu& pCmd, ResponseApdu& pRes) override; - virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, EstablishPaceChannelOutput& pChannelOutput, quint8 pTimeoutSeconds = 60) override; + virtual EstablishPaceChannelOutput establishPaceChannel(PacePasswordId pPasswordId, const QByteArray& pChat, const QByteArray& pCertificateDescription, quint8 pTimeoutSeconds = 60) override; virtual CardReturnCode setEidPin(quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) override; }; diff --git a/src/remote_device/plugin/RemoteReader.cpp b/src/remote_device/plugin/RemoteReader.cpp index f127038..4c31e9b 100644 --- a/src/remote_device/plugin/RemoteReader.cpp +++ b/src/remote_device/plugin/RemoteReader.cpp @@ -33,7 +33,12 @@ RemoteReader::~RemoteReader() Card* RemoteReader::getCard() const { - return mCard.data(); + if (mCard) + { + return mCard.data(); + } + + return nullptr; } @@ -61,8 +66,8 @@ void RemoteReader::update(const IfdStatus& pIfdStatus) if (!pIfdStatus.getCardAvailable()) { qCDebug(card_remote) << "Card removed"; - mReaderInfo.setCardInfo(CardInfo(CardType::NONE)); mCard.reset(); + mReaderInfo.setCardInfo(CardInfo(CardType::NONE)); Q_EMIT fireCardRemoved(getName()); } return; diff --git a/src/remote_device/plugin/RemoteReaderManagerPlugIn.cpp b/src/remote_device/plugin/RemoteReaderManagerPlugIn.cpp index 2347e2c..d6aec31 100644 --- a/src/remote_device/plugin/RemoteReaderManagerPlugIn.cpp +++ b/src/remote_device/plugin/RemoteReaderManagerPlugIn.cpp @@ -71,8 +71,13 @@ void RemoteReaderManagerPlugIn::connectToPairedReaders() void RemoteReaderManagerPlugIn::handleIFDStatus(const QJsonObject& pJsonObject, const QString& pId) { + const auto it = mDispatcherList.find(pId); + if (it == mDispatcherList.end()) + { + return; + } + const auto& remoteDispatcher = *it; IfdStatus ifdStatus(pJsonObject); - const auto& remoteDispatcher = mDispatcherList[pId]; const QString& contextHandle = remoteDispatcher->getContextHandle(); const QString& readerName = ifdStatus.getSlotName() + contextHandle; @@ -168,7 +173,7 @@ void RemoteReaderManagerPlugIn::onContextEstablished(const QString& pIfdName, co else { QMetaObject::invokeMethod(remoteDispatcher.data(), [remoteDispatcher] { - const auto& ifdGetStatus = QSharedPointer::create(); + const QSharedPointer& ifdGetStatus = QSharedPointer::create(); remoteDispatcher->send(ifdGetStatus); }, Qt::QueuedConnection); } @@ -200,7 +205,7 @@ void RemoteReaderManagerPlugIn::onRemoteMessage(RemoteCardMessageType pMessageTy qCWarning(card_remote) << "Received an unexpected message of type:" << pMessageType; const auto& dispatcher = mDispatcherList[pId]; QMetaObject::invokeMethod(dispatcher.data(), [dispatcher] { - const auto& errorMessage = QSharedPointer::create(QString(), ECardApiResult::Minor::AL_Unkown_API_Function); + const QSharedPointer& errorMessage = QSharedPointer::create(QString(), ECardApiResult::Minor::AL_Unkown_API_Function); dispatcher->send(errorMessage); }, Qt::QueuedConnection); break; @@ -263,7 +268,7 @@ void RemoteReaderManagerPlugIn::addRemoteDispatcher(const QSharedPointer::create(QStringLiteral("IFDInterface_WebSocket_v0"), DeviceInfo::getName()); + const QSharedPointer& establishContext = QSharedPointer::create(QStringLiteral("IFDInterface_WebSocket_v0"), DeviceInfo::getName()); pRemoteDispatcher->send(establishContext); }, Qt::QueuedConnection); } @@ -287,12 +292,12 @@ void RemoteReaderManagerPlugIn::startScan(bool pAutoConnect) } -void RemoteReaderManagerPlugIn::stopScan() +void RemoteReaderManagerPlugIn::stopScan(const QString& pError) { const auto remoteClient = Env::getSingleton(); disconnect(remoteClient, &RemoteClient::fireDeviceAppeared, this, &RemoteReaderManagerPlugIn::connectToPairedReaders); mScanTimer.stop(); QMetaObject::invokeMethod(remoteClient, &RemoteClient::stopDetection, Qt::QueuedConnection); removeAllDispatchers(); - ReaderManagerPlugIn::stopScan(); + ReaderManagerPlugIn::stopScan(pError); } diff --git a/src/remote_device/plugin/RemoteReaderManagerPlugIn.h b/src/remote_device/plugin/RemoteReaderManagerPlugIn.h index ccafb81..cecbd31 100644 --- a/src/remote_device/plugin/RemoteReaderManagerPlugIn.h +++ b/src/remote_device/plugin/RemoteReaderManagerPlugIn.h @@ -56,7 +56,7 @@ class RemoteReaderManagerPlugIn virtual QList getReaders() const override; virtual void startScan(bool pAutoConnect) override; - virtual void stopScan() override; + virtual void stopScan(const QString& pError = QString()) override; }; diff --git a/src/secure_storage/SecureStorage.cpp b/src/secure_storage/SecureStorage.cpp index d532491..67a5823 100644 --- a/src/secure_storage/SecureStorage.cpp +++ b/src/secure_storage/SecureStorage.cpp @@ -351,6 +351,12 @@ QMap SecureStorage::readKeySizes(const QJsonObject& pCo { keySizes.insert(QSsl::KeyAlgorithm::Dsa, value); } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + else if (key == QLatin1String("Dh")) + { + keySizes.insert(QSsl::KeyAlgorithm::Dh, value); + } +#endif else if (key == QLatin1String("Ec")) { keySizes.insert(QSsl::KeyAlgorithm::Ec, value); diff --git a/src/secure_storage/SecureStorage.h b/src/secure_storage/SecureStorage.h index bd884a5..120aa2d 100644 --- a/src/secure_storage/SecureStorage.h +++ b/src/secure_storage/SecureStorage.h @@ -7,6 +7,7 @@ #pragma once +#include "Env.h" #include "TlsConfiguration.h" #include @@ -21,7 +22,7 @@ class test_SecureStorage; - +class MockSecureStorage; namespace governikus { @@ -30,7 +31,10 @@ using SignatureAlgorithmPair = QPair(); - mAppUpdateJsonUrl = VersionNumber::getApplicationVersion().isDeveloperVersion() ? secureStorage.getAppcastBetaUpdateUrl() : secureStorage.getAppcastUpdateUrl(); + mAppUpdateJsonUrl = VersionNumber::getApplicationVersion().isDeveloperVersion() ? secureStorage->getAppcastBetaUpdateUrl() : secureStorage->getAppcastUpdateUrl(); } @@ -45,7 +45,7 @@ void AppUpdater::checkAppUpdate(bool pIgnoreNextVersionskip) mIgnoreNextVersionskip = pIgnoreNextVersionskip; mAppUpdateData = AppUpdateData(); - Downloader* downloader = Env::getSingleton(); + auto* downloader = Env::getSingleton(); connect(downloader, &Downloader::fireDownloadSuccess, this, &AppUpdater::onUpdateDownloadFinished); connect(downloader, &Downloader::fireDownloadFailed, this, &AppUpdater::onUpdateDownloadFailed); connect(downloader, &Downloader::fireDownloadUnnecessary, this, &AppUpdater::onUpdateDownloadUnnecessary); @@ -69,7 +69,7 @@ void AppUpdater::skipVersion(const QString& pVersion) void AppUpdater::onUpdateDownloadFinished(const QUrl& pUpdateUrl, const QDateTime& pNewTimestamp, const QByteArray& pData) { - Q_UNUSED(pNewTimestamp); + Q_UNUSED(pNewTimestamp) if (pUpdateUrl == mAppUpdateJsonUrl) { AppUpdateData newData = AppUpdateData::parse(pData); @@ -146,7 +146,7 @@ void AppUpdater::onUpdateDownloadUnnecessary(const QUrl& pUpdateUrl) void AppUpdater::clearDownloaderConnection() { - Downloader* const downloader = Env::getSingleton(); + auto* const downloader = Env::getSingleton(); disconnect(downloader, &Downloader::fireDownloadSuccess, this, &AppUpdater::onUpdateDownloadFinished); disconnect(downloader, &Downloader::fireDownloadFailed, this, &AppUpdater::onUpdateDownloadFailed); disconnect(downloader, &Downloader::fireDownloadUnnecessary, this, &AppUpdater::onUpdateDownloadUnnecessary); diff --git a/src/services/Service.cpp b/src/services/Service.cpp index 557df28..40afbbb 100644 --- a/src/services/Service.cpp +++ b/src/services/Service.cpp @@ -46,11 +46,15 @@ void Service::updateConfigurations() void Service::updateApp(bool pIgnoreNextVersionskip) { +#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) mExplicitSuccessMessage = pIgnoreNextVersionskip; mTimer.start(mOneDayInMs); QMetaObject::invokeMethod(this, [ = ] { doAppUpdate(pIgnoreNextVersionskip); }, Qt::QueuedConnection); +#else + Q_UNUSED(pIgnoreNextVersionskip) +#endif } diff --git a/src/settings/AppSettings.h b/src/settings/AppSettings.h index c300184..79c4fa1 100644 --- a/src/settings/AppSettings.h +++ b/src/settings/AppSettings.h @@ -29,7 +29,6 @@ class AppSettings { Q_OBJECT friend class Env; - friend class ::test_AppSettings; private: bool mUsedAsSdk; diff --git a/src/settings/GeneralSettings.cpp b/src/settings/GeneralSettings.cpp index 772f3f3..d22943d 100644 --- a/src/settings/GeneralSettings.cpp +++ b/src/settings/GeneralSettings.cpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -33,14 +34,12 @@ SETTINGS_NAME(SETTINGS_NAME_LANGUAGE, "language") SETTINGS_NAME(SETTINGS_NAME_SELECTED_UI, "selectedUi") SETTINGS_NAME(SETTINGS_NAME_DEVICE_SURVEY_PENDING, "deviceSurveyPending") SETTINGS_NAME(SETTINGS_NAME_LAST_READER_PLUGIN_TYPE, "lastTechnology") - -#if !defined(Q_OS_IOS) +SETTINGS_NAME(SETTINGS_NAME_IN_APP_NOTIFICATIONS, "showInAppNotifications") SETTINGS_NAME(SETTINGS_NAME_REQUEST_STORE_FEEDBACK, "requestStoreFeedback") -#endif - SETTINGS_NAME(SETTINGS_GROUP_NAME_COMMON, "common") SETTINGS_NAME(SETTINGS_NAME_AUTO, "autoUpdateCheck") SETTINGS_NAME(SETTINGS_NAME_KEYLESS_PASSWORD, "keylessPassword") +SETTINGS_NAME(SETTINGS_NAME_SHUFFLE_SCREEN_KEYBOARD, "shuffleScreenKeyboard") } // namespace GeneralSettings::GeneralSettings() @@ -272,6 +271,7 @@ void GeneralSettings::setLanguage(const QLocale::Language pLanguage) if (pLanguage != getLanguage()) { mStoreGeneral->setValue(SETTINGS_NAME_LANGUAGE(), pLanguage == QLocale::C ? QString() : QLocale(pLanguage).bcp47Name()); + Q_EMIT fireLanguageChanged(); Q_EMIT fireSettingsChanged(); } } @@ -279,7 +279,7 @@ void GeneralSettings::setLanguage(const QLocale::Language pLanguage) QString GeneralSettings::getSelectedUi() const { - return QStringLiteral(DEFAULT_UI); + return mStoreGeneral->value(SETTINGS_NAME_SELECTED_UI(), QStringLiteral(DEFAULT_UI)).toString(); } @@ -317,45 +317,23 @@ void GeneralSettings::setDeviceSurveyPending(bool pDeviceSurveyPending) bool GeneralSettings::askForStoreFeedback() const { -#if defined(Q_OS_IOS) - qCWarning(settings) << "STORE FEEDBACK NOT IMPLEMENTED ON IOS"; - return false; - -#else return !mStoreGeneral->contains(SETTINGS_NAME_REQUEST_STORE_FEEDBACK()); - -#endif } bool GeneralSettings::isRequestStoreFeedback() const { -#if defined(Q_OS_IOS) - qCWarning(settings) << "STORE FEEDBACK NOT IMPLEMENTED ON IOS"; - return false; - -#else - return mStoreGeneral->value(SETTINGS_NAME_REQUEST_STORE_FEEDBACK(), false).toBool(); - -#endif } void GeneralSettings::setRequestStoreFeedback(bool pRequest) { -#if defined(Q_OS_IOS) - Q_UNUSED(pRequest); - qCWarning(settings) << "STORE FEEDBACK NOT IMPLEMENTED ON IOS"; - return; - -#else if (askForStoreFeedback() || pRequest != isRequestStoreFeedback()) { mStoreGeneral->setValue(SETTINGS_NAME_REQUEST_STORE_FEEDBACK(), pRequest); Q_EMIT fireSettingsChanged(); } -#endif } @@ -380,18 +358,30 @@ bool GeneralSettings::isAutoUpdateCheck() const if (autoUpdateCheckIsSetByAdmin()) { mStoreCommon->remove(SETTINGS_NAME_AUTO()); + // Start writing the new path since 1.17, too, so that we can rely on it in a future version. + mStoreGeneral->remove(SETTINGS_NAME_AUTO()); } +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + return mStoreCommon->value(SETTINGS_NAME_AUTO(), false).toBool(); + +#else return mStoreCommon->value(SETTINGS_NAME_AUTO(), true).toBool(); + +#endif } bool GeneralSettings::autoUpdateCheckIsSetByAdmin() const { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + QSettings settings(QSettings::Scope::SystemScope); +#else #ifdef Q_OS_MACOS QSettings settings(QSettings::Scope::SystemScope, QCoreApplication::organizationDomain(), QCoreApplication::applicationName()); #else QSettings settings(QSettings::Scope::SystemScope, QCoreApplication::organizationName(), QCoreApplication::applicationName()); +#endif #endif settings.beginGroup(SETTINGS_GROUP_NAME_COMMON()); @@ -404,6 +394,8 @@ void GeneralSettings::setAutoUpdateCheck(bool pAutoUpdateCheck) if (!autoUpdateCheckIsSetByAdmin() && pAutoUpdateCheck != isAutoUpdateCheck()) { mStoreCommon->setValue(SETTINGS_NAME_AUTO(), pAutoUpdateCheck); + // Start writing the new path since 1.17, too, so that we can rely on it in a future version. + mStoreGeneral->setValue(SETTINGS_NAME_AUTO(), pAutoUpdateCheck); Q_EMIT fireSettingsChanged(); } } @@ -420,6 +412,49 @@ void GeneralSettings::setUseScreenKeyboard(bool pUseScreenKeyboard) if (pUseScreenKeyboard != isUseScreenKeyboard()) { mStoreCommon->setValue(SETTINGS_NAME_KEYLESS_PASSWORD(), pUseScreenKeyboard); + // Start writing the new path since 1.17, too, so that we can rely on it in a future version. + mStoreGeneral->setValue(SETTINGS_NAME_KEYLESS_PASSWORD(), pUseScreenKeyboard); + Q_EMIT fireSettingsChanged(); + } +} + + +bool GeneralSettings::isShuffleScreenKeyboard() const +{ + return mStoreCommon->value(SETTINGS_NAME_SHUFFLE_SCREEN_KEYBOARD(), false).toBool(); +} + + +void GeneralSettings::setShuffleScreenKeyboard(bool pShuffleScreenKeyboard) +{ + if (pShuffleScreenKeyboard != isShuffleScreenKeyboard()) + { + mStoreCommon->setValue(SETTINGS_NAME_SHUFFLE_SCREEN_KEYBOARD(), pShuffleScreenKeyboard); + Q_EMIT fireSettingsChanged(); + } +} + + +bool GeneralSettings::isShowInAppNotifications() const +{ +#if defined(Q_OS_WIN) + return QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows10; + +#elif defined(Q_OS_MACOS) + return false; + +#else + return mStoreGeneral->value(SETTINGS_NAME_IN_APP_NOTIFICATIONS(), true).toBool(); + +#endif +} + + +void GeneralSettings::setShowInAppNotifications(bool pShowInAppNotifications) +{ + if (pShowInAppNotifications != isShowInAppNotifications()) + { + mStoreGeneral->setValue(SETTINGS_NAME_IN_APP_NOTIFICATIONS(), pShowInAppNotifications); Q_EMIT fireSettingsChanged(); } } diff --git a/src/settings/GeneralSettings.h b/src/settings/GeneralSettings.h index dfb56a8..bcbef86 100644 --- a/src/settings/GeneralSettings.h +++ b/src/settings/GeneralSettings.h @@ -36,7 +36,6 @@ class GeneralSettings Q_OBJECT friend class AppSettings; - friend class ::test_GeneralSettings; friend bool operator==(const GeneralSettings& pLeft, const GeneralSettings& pRight); private: @@ -100,6 +99,15 @@ class GeneralSettings bool isUseScreenKeyboard() const; void setUseScreenKeyboard(bool pUseScreenKeyboard); + + bool isShuffleScreenKeyboard() const; + void setShuffleScreenKeyboard(bool pShuffleScreenKeyboard); + + bool isShowInAppNotifications() const; + void setShowInAppNotifications(bool pShowInAppNotifications); + + Q_SIGNALS: + void fireLanguageChanged(); }; diff --git a/src/settings/HistorySettings.h b/src/settings/HistorySettings.h index d6d7f63..5e9f4ba 100644 --- a/src/settings/HistorySettings.h +++ b/src/settings/HistorySettings.h @@ -34,7 +34,6 @@ class HistorySettings { Q_OBJECT friend class AppSettings; - friend class ::test_HistorySettings; private: QSharedPointer mStore; diff --git a/src/settings/RemoteServiceSettings.cpp b/src/settings/RemoteServiceSettings.cpp index 557419e..7d11e67 100644 --- a/src/settings/RemoteServiceSettings.cpp +++ b/src/settings/RemoteServiceSettings.cpp @@ -66,6 +66,7 @@ QString RemoteServiceSettings::getDefaultServerName() QString name = DeviceInfo::getName(); if (name.isEmpty()) { + //: LABEL ALL_PLATFORMS return tr("Remote Reader"); } diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 3eef3e0..2216e76 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -14,11 +14,7 @@ IF(DESKTOP) ADD_SUBDIRECTORY(widget) ENDIF() -IF(DESKTOP) - ADD_SUBDIRECTORY(cli) -ENDIF() - -ADD_SUBDIRECTORY(jsonapi) +ADD_SUBDIRECTORY(json) ADD_SUBDIRECTORY(aidl) ADD_SUBDIRECTORY(websocket) diff --git a/src/ui/aidl/CMakeLists.txt b/src/ui/aidl/CMakeLists.txt index f71a279..a3a33bd 100644 --- a/src/ui/aidl/CMakeLists.txt +++ b/src/ui/aidl/CMakeLists.txt @@ -6,5 +6,5 @@ ADD_PLATFORM_LIBRARY(AusweisAppUiAidl) -TARGET_LINK_LIBRARIES(AusweisAppUiAidl Qt5::Core AusweisAppUiJsonApi AusweisAppGlobal) +TARGET_LINK_LIBRARIES(AusweisAppUiAidl Qt5::Core AusweisAppUiJson AusweisAppGlobal) TARGET_COMPILE_DEFINITIONS(AusweisAppUiAidl PRIVATE QT_STATICPLUGIN) diff --git a/src/ui/aidl/UIPlugInAidl.cpp b/src/ui/aidl/UIPlugInAidl.cpp index 0f9ace3..def477c 100644 --- a/src/ui/aidl/UIPlugInAidl.cpp +++ b/src/ui/aidl/UIPlugInAidl.cpp @@ -32,18 +32,18 @@ QAtomicPointer UIPlugInAidl::instance = nullptr; UIPlugInAidl::UIPlugInAidl() : UIPlugIn() - , mJsonApi(nullptr) + , mJson(nullptr) , mContext() , mWorkflowIsActive() , mInitializationSuccessfull(false) { - if (UILoader::getInstance().load(UIPlugInName::UIPlugInJsonApi)) + if (UILoader::getInstance().load(UIPlugInName::UIPlugInJson)) { - mJsonApi = qobject_cast(UILoader::getInstance().getLoaded(UIPlugInName::UIPlugInJsonApi)); - Q_ASSERT(mJsonApi); - connect(mJsonApi, &UIPlugInJsonApi::fireMessage, this, &UIPlugInAidl::onToSend, Qt::QueuedConnection); + mJson = qobject_cast(UILoader::getInstance().getLoaded(UIPlugInName::UIPlugInJson)); + Q_ASSERT(mJson); + connect(mJson, &UIPlugInJson::fireMessage, this, &UIPlugInAidl::onToSend, Qt::QueuedConnection); - mJsonApi->setEnabled(); + mJson->setEnabled(); mInitializationSuccessfull = true; } else @@ -92,17 +92,17 @@ void UIPlugInAidl::onWorkflowStarted(QSharedPointer pContext) void UIPlugInAidl::onWorkflowFinished(QSharedPointer pContext) { - Q_UNUSED(pContext); + Q_UNUSED(pContext) mContext.clear(); - mJsonApi->blockSignals(false); + mJson->blockSignals(false); mWorkflowIsActive.unlock(); } void UIPlugInAidl::onReceived(const QByteArray& pMessage) { - mJsonApi->doMessageProcessing(pMessage); + mJson->doMessageProcessing(pMessage); } @@ -122,7 +122,7 @@ void UIPlugInAidl::reset() { if (mContext) { - mJsonApi->blockSignals(true); + mJson->blockSignals(true); Q_EMIT mContext->fireCancelWorkflow(); } } @@ -137,7 +137,7 @@ void UIPlugInAidl::onToSend(const QByteArray& pMessage) QAndroidJniObject aidlBinder = QtAndroid::androidService().callObjectMethod("getAidlBinder", "()Lcom/governikus/ausweisapp2/AidlBinder;"); aidlBinder.callMethod("aidlReceive", "(Ljava/lang/String;)V", jsonAndroidString.object()); #else - Q_UNUSED(pMessage); + Q_UNUSED(pMessage) #endif } @@ -156,7 +156,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_com_governikus_ausweisapp2_AidlBinder_resetValidSessionID(JNIEnv* pEnv, jobject pObj) { - Q_UNUSED(pObj); + Q_UNUSED(pObj) UIPlugInAidl* plugin = UIPlugInAidl::getInstance(); if (!plugin->isSuccessfullInitialized()) @@ -178,8 +178,8 @@ JNIEXPORT jstring JNICALL Java_com_governikus_ausweisapp2_AidlBinder_resetValidS JNIEXPORT jboolean JNICALL Java_com_governikus_ausweisapp2_AidlBinder_isSecureRandomPsk(JNIEnv* pEnv, jobject pObj) { - Q_UNUSED(pEnv); - Q_UNUSED(pObj); + Q_UNUSED(pEnv) + Q_UNUSED(pObj) return PskManager::getInstance().isSecureRandomPsk(); } @@ -192,9 +192,9 @@ extern "C" JNIEXPORT void JNICALL Java_com_governikus_ausweisapp2_AidlBinder_aidlSend(JNIEnv* pEnv, jobject pObj, jstring pJson) { - Q_UNUSED(pObj); + Q_UNUSED(pObj) - const char* nativeString = pEnv->GetStringUTFChars(pJson, 0); + const char* const nativeString = pEnv->GetStringUTFChars(pJson, 0); const QString json = QString::fromUtf8(nativeString); pEnv->ReleaseStringUTFChars(pJson, nativeString); diff --git a/src/ui/aidl/UIPlugInAidl.h b/src/ui/aidl/UIPlugInAidl.h index 1f52c55..77d3d63 100644 --- a/src/ui/aidl/UIPlugInAidl.h +++ b/src/ui/aidl/UIPlugInAidl.h @@ -7,7 +7,7 @@ #pragma once #include "UIPlugIn.h" -#include "UIPlugInJsonApi.h" +#include "UIPlugInJson.h" #include #include @@ -23,7 +23,7 @@ class UIPlugInAidl Q_INTERFACES(governikus::UIPlugIn) private: - UIPlugInJsonApi* mJsonApi; + UIPlugInJson* mJson; QSharedPointer mContext; QMutex mWorkflowIsActive; diff --git a/src/ui/aidl/metadata.json b/src/ui/aidl/metadata.json index 54af3dc..ef80e6b 100644 --- a/src/ui/aidl/metadata.json +++ b/src/ui/aidl/metadata.json @@ -1,4 +1,4 @@ { "name" : "UIPlugInAidl", - "dependencies" : ["UIPlugInJsonApi"] + "dependencies" : ["UIPlugInJson"] } diff --git a/src/ui/base/UILoader.h b/src/ui/base/UILoader.h index 9a74ee9..2adf621 100644 --- a/src/ui/base/UILoader.h +++ b/src/ui/base/UILoader.h @@ -16,7 +16,7 @@ namespace governikus { -defineEnumType(UIPlugInName, UIPlugInQml, UIPlugInCli, UIPlugInWidgets, UIPlugInJsonApi, UIPlugInWebSocket, UIPlugInAidl) +defineEnumType(UIPlugInName, UIPlugInQml, UIPlugInWidgets, UIPlugInJson, UIPlugInWebSocket, UIPlugInAidl) class UILoader : public QObject diff --git a/src/ui/base/UIPlugIn.h b/src/ui/base/UIPlugIn.h index 8a1e02f..b0809be 100644 --- a/src/ui/base/UIPlugIn.h +++ b/src/ui/base/UIPlugIn.h @@ -61,6 +61,7 @@ class UIPlugIn void fireShowUserInformation(const QString& pInformationMessage); void fireUiDominationRequest(const UIPlugIn* pUi, const QString& pInformation); void fireUiDominationRelease(); + void fireRestartApplicationRequested(); }; } // namespace governikus diff --git a/src/ui/cli/CMakeLists.txt b/src/ui/cli/CMakeLists.txt deleted file mode 100644 index 3032e8f..0000000 --- a/src/ui/cli/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -##################################################################### -# The cli plugin implements the ui interface for a command line. -# -# It is an experimental interface that can be controled by a -# terminal/console. It is deprecated and should be replaced by -# the WebSocket plugin. -##################################################################### - -ADD_PLATFORM_LIBRARY(AusweisAppUiCli) - -TARGET_LINK_LIBRARIES(AusweisAppUiCli Qt5::Core AusweisAppGlobal AusweisAppCore AusweisAppUi AusweisAppActivationWebservice) -TARGET_COMPILE_DEFINITIONS(AusweisAppUiCli PRIVATE QT_STATICPLUGIN) diff --git a/src/ui/cli/ConsoleReader.cpp b/src/ui/cli/ConsoleReader.cpp deleted file mode 100644 index 01815af..0000000 --- a/src/ui/cli/ConsoleReader.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "ConsoleReader.h" - -#include -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(cli) -Q_DECLARE_LOGGING_CATEGORY(stdinput) - -using namespace governikus; - - -#if defined(Q_OS_WIN) - -void ConsoleInputThread::run() -{ - const HANDLE eventHandles = GetStdHandle(STD_INPUT_HANDLE); - while (!isInterruptionRequested()) - { - if (WSA_WAIT_EVENT_0 != WSAWaitForMultipleEvents(1, &eventHandles, FALSE, 200, TRUE)) - { - continue; - } - - QTextStream input(stdin, QIODevice::ReadOnly); - const auto& line = input.readLine(); - qCDebug(stdinput) << line; - Q_EMIT fireText(line); - } -} - - -ConsoleInputThread::~ConsoleInputThread() -{ - requestInterruption(); - wait(); -} - - -ConsoleReader::ConsoleReader(QObject* pParent) - : QObject(pParent) -{ -} - - -void ConsoleReader::init() -{ - mConsoleInputThread.reset(new ConsoleInputThread()); - connect(mConsoleInputThread.data(), &ConsoleInputThread::fireText, this, &ConsoleReader::fireText); - mConsoleInputThread->start(); -} - - -void ConsoleReader::shutdown() -{ - mConsoleInputThread.reset(); -} - - -bool ConsoleReader::isInputOpen() const -{ - return !mConsoleInputThread.isNull(); -} - - -#else - - -ConsoleReader::ConsoleReader(QObject* pParent) - : QObject(pParent) - , mNotifier() - , mInputOpen(false) -{ -} - - -void ConsoleReader::init() -{ - Q_ASSERT(mNotifier.isNull()); - mNotifier.reset(new QSocketNotifier(fileno(stdin), QSocketNotifier::Read)); - connect(mNotifier.data(), &QSocketNotifier::activated, this, &ConsoleReader::onData); - mInputOpen = true; -} - - -void ConsoleReader::onData() -{ - Q_EMIT fireText(readText()); -} - - -QString ConsoleReader::readText() -{ - Q_ASSERT(!mNotifier.isNull()); - - QTextStream input(stdin, QIODevice::ReadOnly); - const QString& line = input.readLine(); - if (input.status() == QTextStream::ReadPastEnd) - { - mInputOpen = false; - } - return line; -} - - -void ConsoleReader::shutdown() -{ - Q_ASSERT(!mNotifier.isNull()); - mInputOpen = false; - mNotifier->setEnabled(false); - - Q_EMIT fireShutdown(); -} - - -bool ConsoleReader::isInputOpen() const -{ - return mInputOpen; -} - - -#endif diff --git a/src/ui/cli/ConsoleReader.h b/src/ui/cli/ConsoleReader.h deleted file mode 100644 index 12cf272..0000000 --- a/src/ui/cli/ConsoleReader.h +++ /dev/null @@ -1,74 +0,0 @@ -/*! - * \brief Helper to read stdin in non-blocking mode. - * - * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -#if defined(Q_OS_WIN) -#include - -#include -#include -#else -#include -#endif - - -namespace governikus -{ - -#if defined(Q_OS_WIN) - -class ConsoleInputThread - : public QThread -{ - Q_OBJECT - - public: - void run() Q_DECL_OVERRIDE; - virtual ~ConsoleInputThread(); - - Q_SIGNALS: - void fireText(const QString& pData); -}; - -#endif - - -class ConsoleReader - : public QObject -{ - Q_OBJECT - - private: -#if defined(Q_OS_WIN) - QScopedPointer mConsoleInputThread; -#else - QScopedPointer mNotifier; - - bool mInputOpen; - - private Q_SLOTS: - void onData(); -#endif - - public: - ConsoleReader(QObject* pParent = nullptr); - void init(); - void shutdown(); - bool isInputOpen() const; - - QString readText(); - - Q_SIGNALS: - void fireShutdown(); - void fireText(const QString& pData); -}; - - -} // namespace governikus diff --git a/src/ui/cli/UIPlugInCli.cpp b/src/ui/cli/UIPlugInCli.cpp deleted file mode 100644 index 7c321c2..0000000 --- a/src/ui/cli/UIPlugInCli.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "UIPlugInCli.h" - -#include "states/StateEnterPacePassword.h" - -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) -#include "Env.h" -#include "HttpServer.h" -#endif - -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(stdinput) -Q_DECLARE_LOGGING_CATEGORY(cli) - -using namespace governikus; - -UIPlugInCli::UIPlugInCli() - : mReader() - , mAvailableCommands() -{ - addCommand(QStringLiteral("cancel"), &UIPlugInCli::handleCancelWorkflow); - addCommand(QStringLiteral("changepin"), &UIPlugInCli::handleChangePin); - addCommand(QStringLiteral("enter-pin"), &UIPlugInCli::handleEnterPin); - addCommand(QStringLiteral("help"), &UIPlugInCli::handleHelp); - addCommand(QStringLiteral("ping"), &UIPlugInCli::handlePing); - addCommand(QStringLiteral("port"), &UIPlugInCli::handlePort); - addCommand(QStringLiteral("quit"), &UIPlugInCli::handleQuit); - - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::doInput); - mReader.init(); -} - - -UIPlugInCli::~UIPlugInCli() -{ -} - - -void UIPlugInCli::onApplicationStarted() -{ - qCInfo(cli) << "ready"; -} - - -void UIPlugInCli::doShutdown() -{ - mReader.shutdown(); -} - - -void UIPlugInCli::onWorkflowStarted(QSharedPointer pContext) -{ - mContext = pContext; - if (!mContext.isNull()) - { - connect(mContext.data(), &WorkflowContext::fireStateChanged, - this, &UIPlugInCli::onStateChanged); - mContext->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC}); - } -} - - -void UIPlugInCli::onWorkflowFinished(QSharedPointer pContext) -{ - Q_UNUSED(pContext); - if (!mContext.isNull()) - { - mContext->disconnect(this); - mContext.reset(); - } -} - - -void UIPlugInCli::onStateChanged(const QString& pState) -{ - Q_UNUSED(pState); - - bool userInteractionRequired = false; - - if (AbstractState::isState(pState) && mContext->getEstablishPaceChannelType() == PacePasswordId::PACE_PIN) - { - userInteractionRequired = true; - qCInfo(cli) << "enter-pin"; - } - - mContext->setStateApproved(!userInteractionRequired); -} - - -void UIPlugInCli::doInput(const QString& pData) -{ - qCInfo(stdinput) << pData; - - const QStringList chunks = pData.split(QLatin1Char(' ')); - auto func = mAvailableCommands.value(chunks.at(0).toLower()); - if (func) - { - mCurrentCommandArgs = chunks.mid(1); - func(); - } - else - { - qCWarning(cli) << "Unknown command:" << pData; - } -} - - -void UIPlugInCli::handleChangePin() -{ - mOldPin.clear(); - mNewPin.clear(); - - qCDebug(cli) << "Change PIN requested"; - qCInfo(cli) << "Please enter old PIN"; - - disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::doInput); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleOldPinEntered); -} - - -void UIPlugInCli::handleOldPinEntered(const QString& pLine) -{ - const QRegularExpression regexOldPin(QStringLiteral("^[0-9]{5,6}$")); - if (regexOldPin.match(pLine).hasMatch() && mReader.isInputOpen()) - { - mOldPin = pLine; - qCInfo(cli) << "Please enter new PIN"; - disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleOldPinEntered); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEntered); - } - else - { - qCInfo(cli) << "Please enter old PIN"; - } -} - - -void UIPlugInCli::handleNewPinEntered(const QString& pLine) -{ - const QRegularExpression regexNewPin(QStringLiteral("^[0-9]{6}$")); - if (regexNewPin.match(pLine).hasMatch() && mReader.isInputOpen()) - { - mNewPin = pLine; - qCInfo(cli) << "Please enter new PIN again"; - disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEntered); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEnteredAgain); - } - else - { - qCInfo(cli) << "Please enter new PIN"; - } -} - - -void UIPlugInCli::handleNewPinEnteredAgain(const QString& pLine) -{ - if (mNewPin != pLine) - { - qCInfo(cli) << "PINs were not equal"; - qCInfo(cli) << "Please enter new PIN"; - - disconnect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEnteredAgain); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::handleNewPinEntered); - } - else if (mReader.isInputOpen()) - { - qCDebug(cli) << "Start"; - Q_EMIT fireChangePinRequest(); - connect(&mReader, &ConsoleReader::fireText, this, &UIPlugInCli::doInput); - } -} - - -void UIPlugInCli::handleCancelWorkflow() -{ - if (mContext.isNull()) - { - qCInfo(cli) << "error"; - } - else - { - Q_EMIT mContext->fireCancelWorkflow(); - qCInfo(cli) << "ok"; - } -} - - -void UIPlugInCli::handleEnterPin() -{ - if (mCurrentCommandArgs.size() != 1 || mContext.isNull()) - { - qCInfo(cli) << "error"; - } - else - { - mContext->setPin(mCurrentCommandArgs.at(0)); - mContext->setStateApproved(true); - qCInfo(cli) << "ok"; - } -} - - -void UIPlugInCli::handleHelp() -{ - qCInfo(cli) << "Available commands:" << mAvailableCommands.keys().join(QStringLiteral(" | ")); -} - - -void UIPlugInCli::handlePing() -{ - qCInfo(cli) << "Pong!"; -} - - -void UIPlugInCli::handlePort() -{ -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - qCInfo(cli) << "Port:" << Env::getShared()->getServerPort(); -#else - qCInfo(cli) << "Port is undefined"; -#endif -} - - -void UIPlugInCli::handleQuit() -{ - qCInfo(cli) << "Shutdown application..."; - Q_EMIT fireQuitApplicationRequest(); -} diff --git a/src/ui/cli/UIPlugInCli.h b/src/ui/cli/UIPlugInCli.h deleted file mode 100644 index ea0bb85..0000000 --- a/src/ui/cli/UIPlugInCli.h +++ /dev/null @@ -1,72 +0,0 @@ -/*! - * \brief UIPlugIn implementation of CLI. - * - * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "context/WorkflowContext.h" -#include "ConsoleReader.h" -#include "UIPlugIn.h" - -#include -#include - -#include - -namespace governikus -{ - -class UIPlugInCli - : public UIPlugIn -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "governikus.UIPlugIn" FILE "metadata.json") - Q_INTERFACES(governikus::UIPlugIn) - - using MemberFunc = void (UIPlugInCli::*)(); - - private: - QString mOldPin; - QString mNewPin; - ConsoleReader mReader; - QMap > mAvailableCommands; - QSharedPointer mContext; - QStringList mCurrentCommandArgs; - - void addCommand(const QString& pCmd, MemberFunc pFunc) - { - mAvailableCommands.insert(pCmd, std::bind(pFunc, this)); - } - - - void handleCancelWorkflow(); - void handleChangePin(); - void handleEnterPin(); - void handleHelp(); - void handlePing(); - void handlePort(); - void handleQuit(); - - public: - UIPlugInCli(); - virtual ~UIPlugInCli() override; - - public Q_SLOTS: - virtual void onApplicationStarted() override; - - virtual void doShutdown() override; - - private Q_SLOTS: - void doInput(const QString& pData); - virtual void onWorkflowStarted(QSharedPointer pContext) override; - virtual void onWorkflowFinished(QSharedPointer pContext) override; - void onStateChanged(const QString& pState); - - void handleOldPinEntered(const QString& pLine); - void handleNewPinEntered(const QString& pLine); - void handleNewPinEnteredAgain(const QString& pLine); -}; - -} // namespace governikus diff --git a/src/ui/cli/metadata.json b/src/ui/cli/metadata.json deleted file mode 100644 index c850e16..0000000 --- a/src/ui/cli/metadata.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name" : "UIPlugInCli", - "dependencies" : [] -} diff --git a/src/ui/common/CMakeLists.txt b/src/ui/common/CMakeLists.txt index d33b64c..fbe6845 100644 --- a/src/ui/common/CMakeLists.txt +++ b/src/ui/common/CMakeLists.txt @@ -4,7 +4,7 @@ ADD_PLATFORM_LIBRARY(AusweisAppUiCommon) -TARGET_LINK_LIBRARIES(AusweisAppUiCommon Qt5::Core Qt5::Gui AusweisAppGlobal) +TARGET_LINK_LIBRARIES(AusweisAppUiCommon Qt5::Core Qt5::Gui AusweisAppGlobal AusweisAppCard AusweisAppRemoteDevice) IF(DESKTOP) TARGET_LINK_LIBRARIES(AusweisAppUiCommon Qt5::Widgets) diff --git a/src/ui/common/Email.cpp b/src/ui/common/Email.cpp new file mode 100644 index 0000000..429486e --- /dev/null +++ b/src/ui/common/Email.cpp @@ -0,0 +1,50 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "Email.h" + +#include "BuildHelper.h" +#include "GlobalStatus.h" +#include "LogHandler.h" + +#include + +namespace governikus +{ + +QString generateMailBody(const GlobalStatus& pStatus) +{ + const auto& logHandler = Env::getSingleton(); + QStringList mailBody(QObject::tr("Please describe the error that occurred.")); + + if (logHandler->useLogfile()) + { + mailBody << QObject::tr("You may want to attach the logfile which can be saved from the error dialog."); + } + + const QString newLine = QLatin1String("\n"); + mailBody << newLine; + + const auto& systemInfo = BuildHelper::getInformationHeader(); + for (const auto& info : systemInfo) + { + const auto first = QString::fromUtf8(QUrl::toPercentEncoding(info.first)); + const auto second = QString::fromUtf8(QUrl::toPercentEncoding(info.second)); + mailBody << first + QStringLiteral(": ") + second; + } + + mailBody << newLine + QObject::tr("Error code") + QLatin1Char(':'); + mailBody << getEnumName(pStatus.getStatusCode()); + + if (logHandler->hasCriticalLog()) + { + const QString criticalMessages = QString::fromUtf8(logHandler->getCriticalLogWindow()); + mailBody << newLine + QObject::tr("Critical errors:") + newLine + criticalMessages; + } + + return mailBody.join(newLine); +} + + +} // namespace governikus diff --git a/src/ui/common/Email.h b/src/ui/common/Email.h new file mode 100644 index 0000000..63beb2c --- /dev/null +++ b/src/ui/common/Email.h @@ -0,0 +1,16 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class GlobalStatus; + +QString generateMailBody(const GlobalStatus&); + +} // namespace governikus diff --git a/src/ui/common/HelpAction.cpp b/src/ui/common/HelpAction.cpp index 5286e28..270337c 100644 --- a/src/ui/common/HelpAction.cpp +++ b/src/ui/common/HelpAction.cpp @@ -33,7 +33,8 @@ const QMap HelpAction::mHelpMapping = { {QStringLiteral("generalTab"), QStringLiteral("settings-general.html")}, {QStringLiteral("pinTab"), QStringLiteral("settings-pin-management.html")}, {QStringLiteral("readerDeviceTab"), QStringLiteral("settings-reader-detection.html")}, - {QStringLiteral("stepChooseCardGui"), QStringLiteral("settings-reader-detection.html")} + {QStringLiteral("stepChooseCardGui"), QStringLiteral("settings-reader-detection.html")}, + {QStringLiteral("index"), QStringLiteral("index.html")} }; diff --git a/src/ui/common/ReaderDriverModel.cpp b/src/ui/common/ReaderDriverModel.cpp new file mode 100644 index 0000000..b2c7a47 --- /dev/null +++ b/src/ui/common/ReaderDriverModel.cpp @@ -0,0 +1,298 @@ +/*! + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "ReaderDriverModel.h" + +#include "AppSettings.h" +#include "Env.h" +#include "GeneralSettings.h" +#include "HelpAction.h" +#include "LanguageLoader.h" +#include "ReaderConfiguration.h" +#include "ReaderDetector.h" +#include "ReaderManager.h" + +using namespace governikus; + + +ReaderDriverModel::ReaderDriverModel(QObject* pParent) + : QAbstractTableModel(pParent) + , mKnownDrivers() + , mConnectedReaders() + , mConnectedReadersUpdateTime() +{ + const GeneralSettings& generalSettings = Env::getSingleton()->getGeneralSettings(); + connect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &ReaderDriverModel::fireLanguageChanged); + connect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &ReaderDriverModel::onUpdateContent); + + const ReaderManager* const readerManager = Env::getSingleton(); + + connect(readerManager, &ReaderManager::fireReaderAdded, this, &ReaderDriverModel::onUpdateContent); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &ReaderDriverModel::onUpdateContent); + connect(Env::getSingleton(), &ReaderConfiguration::fireUpdated, this, &ReaderDriverModel::onUpdateContent); + connect(Env::getSingleton(), &ReaderDetector::fireReaderChangeDetected, this, &ReaderDriverModel::onUpdateContent); + connect(Env::getSingleton(), &AppSettings::fireSettingsChanged, this, &ReaderDriverModel::onUpdateContent); + onUpdateContent(); +} + + +void ReaderDriverModel::collectReaderData() +{ + mConnectedReaders.clear(); + + const QVector installedReaders = Env::getSingleton()->getReaderInfos(ReaderFilter({ + ReaderManagerPlugInType::PCSC + , ReaderManagerPlugInType::BLUETOOTH + , ReaderManagerPlugInType::NFC + })); + + for (const auto& installedReader : installedReaders) + { + const auto& readerSettingsInfo = installedReader.getReaderConfigurationInfo(); + if (!readerSettingsInfo.getUrl().isEmpty()) + { + mKnownDrivers += readerSettingsInfo; + mConnectedReaders += readerSettingsInfo; + } + } + + QVector readersWithoutDriver; + const auto& attachedSupportedDevices = Env::getSingleton()->getAttachedSupportedDevices(); + for (const auto& info : attachedSupportedDevices) + { + if (!mConnectedReaders.contains(info)) + { + readersWithoutDriver.append(info); + } + } + mConnectedReaders += readersWithoutDriver; +} + + +bool ReaderDriverModel::indexIsValid(const QModelIndex& pIndex) const +{ + if (!pIndex.isValid()) + { + Q_ASSERT(false && "Invoked with an invalid QModelIndex."); + return false; + } + + if (pIndex.row() >= rowCount() || pIndex.column() >= columnCount()) + { + Q_ASSERT(false && "Invoked with a row or a column which is out of bounds."); + return false; + } + + return true; +} + + +QString ReaderDriverModel::getStatus(const ReaderConfigurationInfo& pReaderConfigurationInfo) const +{ + if (mConnectedReaders.isEmpty()) + { + //: LABEL ALL_PLATFORMS + return tr("Not connected"); + } + + if (mKnownDrivers.contains(pReaderConfigurationInfo)) + { + //: LABEL ALL_PLATFORMS + return tr("Driver installed"); + } + + //: LABEL ALL_PLATFORMS + return tr("No driver installed"); +} + + +void ReaderDriverModel::onUpdateContent() +{ + beginResetModel(); + + collectReaderData(); + mConnectedReadersUpdateTime = QTime::currentTime(); + + endResetModel(); + + Q_EMIT fireModelChanged(); +} + + +QVariant ReaderDriverModel::headerData(int pSection, Qt::Orientation pOrientation, int pRole) const +{ + if (pRole == Qt::DisplayRole && pOrientation == Qt::Horizontal) + { + switch (pSection) + { + case ColumnId::ReaderName: + //: LABEL ALL_PLATFORMS + return tr("Card reader"); + + case ColumnId::ReaderStatus: + //: LABEL ALL_PLATFORMS + return tr("Status"); + + default: + return QVariant(); + } + } + return QVariant(); +} + + +int ReaderDriverModel::rowCount(const QModelIndex&) const +{ + return mConnectedReaders.size(); +} + + +int ReaderDriverModel::columnCount(const QModelIndex&) const +{ + return NUMBER_OF_COLUMNS; +} + + +QVariant ReaderDriverModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (!indexIsValid(pIndex)) + { + return QVariant(); + } + + const auto& reader = mConnectedReaders.at(pIndex.row()); + switch (pRole) + { + case Qt::DisplayRole: + switch (pIndex.column()) + { + case ColumnId::ReaderName: + return reader.getName(); + + case ColumnId::ReaderStatus: + return getStatus(reader); + + default: + return QVariant(); + } + + case READER_NAME: + return reader.getName(); + + case READER_STATUS: + return getStatus(reader); + + case READER_IMAGE_PATH: + return getReaderImageUrl(pIndex); + + case READER_HTML_DESCRIPTION: + return getHTMLDescription(pIndex); + + case READER_DRIVER_URL: + return mConnectedReaders.at(pIndex.row()).getUrl(); + + case READER_INSTALLED_AND_SUPPORTED: + return isInstalledSupportedReader(pIndex); + + default: + return QVariant(); + } +} + + +QHash ReaderDriverModel::roleNames() const +{ + QHash roles = QAbstractTableModel::roleNames(); + roles.insert(READER_NAME, "readerName"); + roles.insert(READER_STATUS, "readerStatus"); + roles.insert(READER_IMAGE_PATH, "readerImagePath"); + roles.insert(READER_HTML_DESCRIPTION, "readerHTMLDescription"); + roles.insert(READER_DRIVER_URL, "readerDriverUrl"); + roles.insert(READER_INSTALLED_AND_SUPPORTED, "readerInstalledAndSupported"); + return roles; +} + + +QString ReaderDriverModel::getNoReaderFoundIconPath() const +{ + return ReaderConfiguration::getNoReaderFoundIconPath(); +} + + +QUrl ReaderDriverModel::getReaderImageUrl(const QModelIndex& pIndex) const +{ + return mConnectedReaders.at(pIndex.row()).getIcon()->lookupUrl(); +} + + +QString ReaderDriverModel::getReaderImagePath(const QModelIndex& pIndex) const +{ + if (!indexIsValid(pIndex)) + { + return QString(); + } + + return mConnectedReaders.at(pIndex.row()).getIcon()->lookupPath(); +} + + +QString ReaderDriverModel::getHTMLDescription(const QModelIndex& pIndex) const +{ + if (!indexIsValid(pIndex)) + { + return QString(); + } + + if (mConnectedReaders.isEmpty()) + { + return QString(); + } + + if (mKnownDrivers.contains(mConnectedReaders.at(pIndex.row()))) + { + //: LABEL ALL_PLATFORMS + return tr("Card reader ready for use."); + } + + //: INFO ALL_PLATFORMS The driver for card reader needs to be installed, the download link is provided in the message. + return tr("Please download and install the driver you can find at: %1"). + arg(QStringLiteral("%1").arg(mConnectedReaders.at(pIndex.row()).getUrl())); +} + + +QString ReaderDriverModel::getEmptyListDescriptionString() const +{ + const QString& url = HelpAction::getOnlineUrl(QStringLiteral("readerDeviceTab")); + //: Is embedded in a sentence. + const QString& hyperlink = QStringLiteral("%2").arg(url, tr("online help")); + //: INFO ALL_PLATFORMS No card reader was found, the message contains a link to the installation section of the manual. + return tr("No connected card reader found. See %1 for installation of card readers.").arg(hyperlink); +} + + +bool ReaderDriverModel::isInstalledSupportedReader(const QModelIndex& pIndex) const +{ + if (!indexIsValid(pIndex)) + { + return false; + } + + const auto& readerSettingsInfo = mConnectedReaders.at(pIndex.row()); + const bool knownDriver = mKnownDrivers.contains(readerSettingsInfo); + const bool knownReader = readerSettingsInfo.isKnownReader(); + return knownDriver && knownReader; +} + + +QString ReaderDriverModel::getLastUpdatedInformation() const +{ + if (!mConnectedReadersUpdateTime.isValid()) + { + return QString(); + } + + const auto& updateTime = LanguageLoader::getInstance().getUsedLocale().toString(mConnectedReadersUpdateTime, tr("hh:mm:ss AP")); + //: LABEL ALL_PLATFORMS + return tr("The list of card readers was last updated at %1.").arg(updateTime); +} diff --git a/src/ui/widget/ReaderDriverModel.h b/src/ui/common/ReaderDriverModel.h similarity index 58% rename from src/ui/widget/ReaderDriverModel.h rename to src/ui/common/ReaderDriverModel.h index d983760..67cd23b 100644 --- a/src/ui/widget/ReaderDriverModel.h +++ b/src/ui/common/ReaderDriverModel.h @@ -9,7 +9,9 @@ #include "ReaderDetector.h" #include +#include #include +#include #include @@ -21,38 +23,60 @@ class ReaderDriverModel { Q_OBJECT + Q_PROPERTY(QString noReaderFoundIconPath READ getNoReaderFoundIconPath CONSTANT) + Q_PROPERTY(QString emptyListDescriptionString READ getEmptyListDescriptionString NOTIFY fireLanguageChanged) + Q_PROPERTY(QString lastUpdatedInformation READ getLastUpdatedInformation NOTIFY fireModelChanged) + private: const int NUMBER_OF_COLUMNS = 2; QSet mKnownDrivers; QVector mConnectedReaders; + QTime mConnectedReadersUpdateTime; QString getStatus(const ReaderConfigurationInfo& pReaderConfigurationInfo) const; void collectReaderData(); + bool indexIsValid(const QModelIndex& pIndex) const; + + QUrl getReaderImageUrl(const QModelIndex& pIndex) const; + public: enum ColumnId : int { ReaderName = 0, ReaderStatus = 1 }; + enum UserRoles + { + READER_NAME = Qt::UserRole + 1, + READER_STATUS, + READER_IMAGE_PATH, + READER_HTML_DESCRIPTION, + READER_DRIVER_URL, + READER_INSTALLED_AND_SUPPORTED + }; ReaderDriverModel(QObject* pParent = nullptr); virtual QVariant headerData(int pSection, Qt::Orientation pOrientation, int pRole) const override; virtual int rowCount(const QModelIndex& pParent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& pParent = QModelIndex()) const override; virtual QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + QHash roleNames() const override; - const ReaderConfigurationInfo& getReaderConfigurationInfo(const QModelIndex& pIndex) const; + QString getReaderImagePath(const QModelIndex& pIndex) const; + QString getNoReaderFoundIconPath() const; QString getHTMLDescription(const QModelIndex& pIndex) const; + QString getEmptyListDescriptionString() const; bool isInstalledSupportedReader(const QModelIndex& pIndex) const; + QString getLastUpdatedInformation() const; public Q_SLOTS: void onUpdateContent(); Q_SIGNALS: void fireModelChanged(); - + void fireLanguageChanged(); }; diff --git a/src/remote_device/RemoteDeviceModel.cpp b/src/ui/common/RemoteDeviceModel.cpp similarity index 69% rename from src/remote_device/RemoteDeviceModel.cpp rename to src/ui/common/RemoteDeviceModel.cpp index 5661495..901fa0b 100644 --- a/src/remote_device/RemoteDeviceModel.cpp +++ b/src/ui/common/RemoteDeviceModel.cpp @@ -5,6 +5,9 @@ #include "RemoteDeviceModel.h" #include "AppSettings.h" +#include "Env.h" +#include "GeneralSettings.h" +#include "HelpAction.h" #include "LanguageLoader.h" #include "RemoteClient.h" @@ -13,7 +16,7 @@ using namespace governikus; -RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString pDeviceName, const QString pId, QSharedPointer& pRemoteDeviceListEntry) +RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString& pDeviceName, const QString& pId, QSharedPointer& pRemoteDeviceListEntry) : mDeviceName(pDeviceName) , mId(pId) , mPaired(false) @@ -25,19 +28,19 @@ RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString pDeviceName, const } -RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString pDeviceName, const QString pId, bool pPaired, bool pNetworkVisible, bool pSupported, const QDateTime& pLastConnected) +RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString& pDeviceName, const QString& pId, bool pPaired, bool pNetworkVisible, bool pSupported, const QDateTime& pLastConnected, QSharedPointer& pRemoteDeviceListEntry) : mDeviceName(pDeviceName) , mId(pId) , mPaired(pPaired) , mNetworkVisible(pNetworkVisible) , mSupported(pSupported) , mLastConnected(pLastConnected) - , mRemoteDeviceListEntry(nullptr) + , mRemoteDeviceListEntry(pRemoteDeviceListEntry) { } -RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString pDeviceName) +RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QString& pDeviceName) : mDeviceName(pDeviceName) , mId() , mPaired(false) @@ -79,7 +82,7 @@ const QString& RemoteDeviceModelEntry::getId() const } -void RemoteDeviceModelEntry::setId(QString pId) +void RemoteDeviceModelEntry::setId(const QString& pId) { mId = pId; } @@ -91,6 +94,16 @@ bool RemoteDeviceModelEntry::isNetworkVisible() const } +int RemoteDeviceModelEntry::getLinkQuality() const +{ + if (mRemoteDeviceListEntry.isNull()) + { + return 0; + } + return mRemoteDeviceListEntry->getPercentSeen(); +} + + bool RemoteDeviceModelEntry::isSupported() const { return mSupported; @@ -121,14 +134,22 @@ RemoteDeviceModel::RemoteDeviceModel(QObject* pParent, bool pShowPairedReaders, , mAllRemoteReaders() , mShowPairedReaders(pShowPairedReaders) , mShowUnpairedReaders(pShowUnpairedReaders) + , mTimer() { - RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + AppSettings* const appSettings = Env::getSingleton(); + + const GeneralSettings& generalSettings = appSettings->getGeneralSettings(); + connect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &RemoteDeviceModel::fireLanguageChanged); + connect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &RemoteDeviceModel::onKnownRemoteReadersChanged); + + const RemoteServiceSettings& settings = appSettings->getRemoteServiceSettings(); connect(&settings, &RemoteServiceSettings::fireTrustedRemoteInfosChanged, this, &RemoteDeviceModel::onKnownRemoteReadersChanged); onKnownRemoteReadersChanged(); const auto& remoteClient = Env::getSingleton(); - connect(remoteClient, &RemoteClient::fireDeviceAppeared, this, &RemoteDeviceModel::constructReaderList); - connect(remoteClient, &RemoteClient::fireDeviceVanished, this, &RemoteDeviceModel::constructReaderList); + connect(remoteClient, &RemoteClient::fireDeviceAppeared, this, &RemoteDeviceModel::onConstructReaderList); + connect(remoteClient, &RemoteClient::fireDeviceUpdated, this, &RemoteDeviceModel::onConstructReaderList); + connect(remoteClient, &RemoteClient::fireDeviceVanished, this, &RemoteDeviceModel::onConstructReaderList); } @@ -136,18 +157,40 @@ QHash RemoteDeviceModel::roleNames() const { QHash roles = QAbstractItemModel::roleNames(); roles.insert(REMOTE_DEVICE_NAME, QByteArrayLiteral("remoteDeviceName")); + roles.insert(REMOTE_DEVICE_STATUS, QByteArrayLiteral("remoteDeviceStatus")); roles.insert(LAST_CONNECTED, QByteArrayLiteral("lastConnected")); roles.insert(DEVICE_ID, QByteArrayLiteral("deviceId")); roles.insert(IS_NETWORK_VISIBLE, QByteArrayLiteral("isNetworkVisible")); roles.insert(IS_SUPPORTED, QByteArrayLiteral("isSupported")); + roles.insert(IS_PAIRED, QByteArrayLiteral("isPaired")); + roles.insert(LINK_QUALITY, QByteArrayLiteral("linkQualityInPercent")); return roles; } +bool RemoteDeviceModel::indexIsValid(const QModelIndex& pIndex) const +{ + if (!pIndex.isValid()) + { + Q_ASSERT(false && "Invoked with an invalid QModelIndex."); + return false; + } + + if (pIndex.row() >= rowCount() || pIndex.column() >= columnCount()) + { + Q_ASSERT(false && "Invoked with a row or a column which is out of bounds."); + return false; + } + + return true; +} + + QString RemoteDeviceModel::getStatus(const RemoteDeviceModelEntry& pRemoteDeviceModelEntry) const { if (mAllRemoteReaders.isEmpty()) { + //: LABEL ALL_PLATFORMS return tr("Not connected"); } @@ -157,18 +200,23 @@ QString RemoteDeviceModel::getStatus(const RemoteDeviceModelEntry& pRemoteDevice { if (pRemoteDeviceModelEntry.isSupported()) { - return tr("Paired and available"); + //: LABEL ALL_PLATFORMS + return tr("Available"); } + //: LABEL ALL_PLATFORMS return tr("Paired, but unsupported"); } + //: LABEL ALL_PLATFORMS return tr("Paired, but unavailable"); } if (!pRemoteDeviceModelEntry.isSupported()) { - return tr("Unsupported version"); + //: LABEL ALL_PLATFORMS + return tr("Unsupported"); } + //: LABEL ALL_PLATFORMS return tr("Not paired"); } @@ -180,9 +228,11 @@ QVariant RemoteDeviceModel::headerData(int pSection, Qt::Orientation pOrientatio switch (pSection) { case ColumnId::ReaderName: + //: LABEL ALL_PLATFORMS return tr("Device"); case ColumnId::ReaderStatus: + //: LABEL ALL_PLATFORMS return tr("Status"); default: @@ -207,6 +257,11 @@ int RemoteDeviceModel::columnCount(const QModelIndex&) const QVariant RemoteDeviceModel::data(const QModelIndex& pIndex, int pRole) const { + if (!indexIsValid(pIndex)) + { + return QVariant(); + } + const auto& reader = mAllRemoteReaders.at(pIndex.row()); switch (pRole) { @@ -223,6 +278,9 @@ QVariant RemoteDeviceModel::data(const QModelIndex& pIndex, int pRole) const case REMOTE_DEVICE_NAME: return reader.getDeviceName(); + case REMOTE_DEVICE_STATUS: + return getStatus(reader); + case LAST_CONNECTED: { const auto& locale = LanguageLoader::getInstance().getUsedLocale(); @@ -239,27 +297,30 @@ QVariant RemoteDeviceModel::data(const QModelIndex& pIndex, int pRole) const case IS_SUPPORTED: return reader.isSupported(); - default: - return QVariant(); + case IS_PAIRED: + return isPaired(pIndex); + + case LINK_QUALITY: + return reader.getLinkQuality(); } - Q_UNREACHABLE(); + return QVariant(); } const QSharedPointer RemoteDeviceModel::getRemoteDeviceListEntry(const QModelIndex& pIndex) const { - const int row = pIndex.row(); - if (row < 0 || row >= mAllRemoteReaders.size()) + if (!indexIsValid(pIndex)) { return QSharedPointer(); } + const int row = pIndex.row(); return mAllRemoteReaders.at(row).getRemoteDeviceListEntry(); } -const QSharedPointer RemoteDeviceModel::getRemoteDeviceListEntry(QString pDeviceId) const +const QSharedPointer RemoteDeviceModel::getRemoteDeviceListEntry(const QString& pDeviceId) const { for (const auto& device : mAllRemoteReaders) { @@ -274,17 +335,27 @@ const QSharedPointer RemoteDeviceModel::getRemoteDeviceLi bool RemoteDeviceModel::isPaired(const QModelIndex& pIndex) const { + if (!indexIsValid(pIndex)) + { + return false; + } + return mAllRemoteReaders.at(pIndex.row()).isPaired(); } bool RemoteDeviceModel::isSupported(const QModelIndex& pIndex) const { + if (!indexIsValid(pIndex)) + { + return false; + } + return mAllRemoteReaders.at(pIndex.row()).isSupported(); } -void RemoteDeviceModel::onWidgetShown() +void RemoteDeviceModel::onUiShown() { if (!mShowUnpairedReaders) { @@ -293,11 +364,11 @@ void RemoteDeviceModel::onWidgetShown() qDebug() << "Starting Remote Device Detection"; Env::getSingleton()->startDetection(); - constructReaderList(); + onConstructReaderList(); } -void RemoteDeviceModel::onWidgetHidden() +void RemoteDeviceModel::onUiHidden() { if (!mShowUnpairedReaders) { @@ -306,7 +377,7 @@ void RemoteDeviceModel::onWidgetHidden() qDebug() << "Stopping Remote Device Detection"; Env::getSingleton()->stopDetection(); - constructReaderList(); + onConstructReaderList(); } @@ -321,16 +392,16 @@ void RemoteDeviceModel::onKnownRemoteReadersChanged() mPairedReaders[reader.getFingerprint()] = reader; } - constructReaderList(); + onConstructReaderList(); } -void RemoteDeviceModel::constructReaderList() +void RemoteDeviceModel::onConstructReaderList() { beginResetModel(); mAllRemoteReaders.clear(); - RemoteClient* const remoteClient = Env::getSingleton(); + auto* const remoteClient = Env::getSingleton(); if (mShowPairedReaders) { @@ -340,12 +411,15 @@ void RemoteDeviceModel::constructReaderList() { bool networkVisible = false; bool supported = true; + QSharedPointer deviceListEntry; + for (const auto& announcingDevice : announcingDevices) { if (announcingDevice && announcingDevice->getRemoteDeviceDescriptor().getIfdId() == pairedReader.getFingerprint()) { networkVisible = true; supported = announcingDevice->getRemoteDeviceDescriptor().isSupported(); + deviceListEntry = announcingDevice; break; } @@ -371,7 +445,8 @@ void RemoteDeviceModel::constructReaderList() , true , networkVisible , supported - , pairedReader.getLastConnected()); + , pairedReader.getLastConnected() + , deviceListEntry); mAllRemoteReaders.append(newEntry); } } @@ -401,6 +476,11 @@ void RemoteDeviceModel::constructReaderList() void RemoteDeviceModel::forgetDevice(const QModelIndex& pIndex) { + if (!indexIsValid(pIndex)) + { + return; + } + auto& modelEntry = mAllRemoteReaders.at(pIndex.row()); const QString& id = modelEntry.getId(); @@ -415,9 +495,19 @@ void RemoteDeviceModel::forgetDevice(const QString& pDeviceId) } +QString RemoteDeviceModel::getEmptyListDescriptionString() const +{ + const QString& url = HelpAction::getOnlineUrl(QStringLiteral("readerDeviceTab")); + //: Is embedded in a sentence. + const QString& hyperlink = QStringLiteral("%2").arg(url, tr("online help")); + //: INFO ALL_PLATFORMS No smartphone with enabled remote service was found on the same network. + return tr("No smartphone with enabled remote service found. See %1 for details of use.").arg(hyperlink); +} + + void RemoteDeviceModel::onDeviceDisconnected(GlobalStatus::Code pCloseCode, const QString& pId) { - Q_UNUSED(pCloseCode); - Q_UNUSED(pId); - constructReaderList(); + Q_UNUSED(pCloseCode) + Q_UNUSED(pId) + onConstructReaderList(); } diff --git a/src/remote_device/RemoteDeviceModel.h b/src/ui/common/RemoteDeviceModel.h similarity index 75% rename from src/remote_device/RemoteDeviceModel.h rename to src/ui/common/RemoteDeviceModel.h index 09588a3..e958118 100644 --- a/src/remote_device/RemoteDeviceModel.h +++ b/src/ui/common/RemoteDeviceModel.h @@ -36,15 +36,16 @@ class RemoteDeviceModelEntry QSharedPointer mRemoteDeviceListEntry; public: - RemoteDeviceModelEntry(const QString pDeviceName, const QString mId, QSharedPointer& pRemoteDeviceListEntry); - RemoteDeviceModelEntry(const QString pDeviceName, const QString mId, bool pPaired, bool pNetworkVisible, bool pSupported, const QDateTime& pLastConnected); - RemoteDeviceModelEntry(const QString pDeviceName = QStringLiteral("UnknownReader")); + RemoteDeviceModelEntry(const QString& pDeviceName, const QString& mId, QSharedPointer& pRemoteDeviceListEntry); + RemoteDeviceModelEntry(const QString& pDeviceName, const QString& mId, bool pPaired, bool pNetworkVisible, bool pSupported, const QDateTime& pLastConnected, QSharedPointer& pRemoteDeviceListEntry); + RemoteDeviceModelEntry(const QString& pDeviceName = QStringLiteral("UnknownReader")); bool isPaired() const; void setPaired(bool pPaired); const QString& getId() const; - void setId(QString pId); + void setId(const QString& pId); bool isNetworkVisible() const; + int getLinkQuality() const; bool isSupported() const; void setNetworkVisible(bool pNetworkVisible); const QDateTime& getLastConnected() const; @@ -60,6 +61,8 @@ class RemoteDeviceModel { Q_OBJECT + Q_PROPERTY(QString emptyListDescriptionString READ getEmptyListDescriptionString NOTIFY fireLanguageChanged) + private: friend class ::test_RemoteDeviceModel; @@ -69,18 +72,26 @@ class RemoteDeviceModel QVector mAllRemoteReaders; const bool mShowPairedReaders; const bool mShowUnpairedReaders; + QTimer mTimer; + + bool indexIsValid(const QModelIndex& pIndex) const; QString getStatus(const RemoteDeviceModelEntry& pRemoteDeviceModelEntry) const; - void constructReaderList(); + + private Q_SLOTS: + void onConstructReaderList(); public: enum SettingsRemoteRoles { REMOTE_DEVICE_NAME = Qt::UserRole + 1, + REMOTE_DEVICE_STATUS, LAST_CONNECTED, DEVICE_ID, IS_NETWORK_VISIBLE, - IS_SUPPORTED + IS_SUPPORTED, + IS_PAIRED, + LINK_QUALITY }; enum ColumnId : int @@ -98,21 +109,23 @@ class RemoteDeviceModel virtual QHash roleNames() const override; const QSharedPointer getRemoteDeviceListEntry(const QModelIndex& pIndex) const; - const QSharedPointer getRemoteDeviceListEntry(QString pDeviceId) const; + const QSharedPointer getRemoteDeviceListEntry(const QString& pDeviceId) const; bool isPaired(const QModelIndex& pIndex) const; bool isSupported(const QModelIndex& pIndex) const; void forgetDevice(const QModelIndex& pIndex); void forgetDevice(const QString& pDeviceId); + QString getEmptyListDescriptionString() const; + public Q_SLOTS: - void onWidgetShown(); - void onWidgetHidden(); + void onUiShown(); + void onUiHidden(); void onKnownRemoteReadersChanged(); void onDeviceDisconnected(GlobalStatus::Code pCloseCode, const QString& pId); Q_SIGNALS: void fireModelChanged(); - + void fireLanguageChanged(); }; diff --git a/src/ui/common/TrayIcon.cpp b/src/ui/common/TrayIcon.cpp index 660f7dd..46c0d5b 100644 --- a/src/ui/common/TrayIcon.cpp +++ b/src/ui/common/TrayIcon.cpp @@ -60,6 +60,12 @@ TrayIcon::~TrayIcon() } +const QIcon& TrayIcon::getIcon() const +{ + return mIcon; +} + + void TrayIcon::create() { #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) @@ -71,12 +77,14 @@ void TrayIcon::create() const auto trayIconMenu = new QMenu(nullptr); #if defined(Q_OS_MACOS) + //: LABEL DESKTOP QAction* showApplicationAction = new QAction(tr("Open"), trayIconMenu); connect(showApplicationAction, &QAction::triggered, this, &TrayIcon::fireShow); trayIconMenu->addAction(showApplicationAction); trayIconMenu->addSeparator(); #endif + //: LABEL DESKTOP const auto quitAction = new QAction(tr("Exit AusweisApp2"), trayIconMenu); connect(quitAction, &QAction::triggered, this, &TrayIcon::fireQuit); trayIconMenu->addAction(quitAction); @@ -89,7 +97,8 @@ void TrayIcon::create() mTrayIcon->setToolTip(QCoreApplication::applicationName()); mTrayIcon->show(); - mTrayIcon->showMessage(QString(), tr("AusweisApp2 was started."), mIcon, 3000); + //: LABEL DESKTOP + showMessage(QString(), tr("AusweisApp2 was started.")); #endif } @@ -103,3 +112,14 @@ void TrayIcon::hide() } #endif } + + +void TrayIcon::showMessage(const QString& pTitle, const QString& pMessage) +{ +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + mTrayIcon->showMessage(pTitle, pMessage, mIcon, 3000); +#else + Q_UNUSED(pTitle); + Q_UNUSED(pMessage); +#endif +} diff --git a/src/ui/common/TrayIcon.h b/src/ui/common/TrayIcon.h index 26af8f1..c67611d 100644 --- a/src/ui/common/TrayIcon.h +++ b/src/ui/common/TrayIcon.h @@ -34,9 +34,12 @@ class TrayIcon TrayIcon(); ~TrayIcon(); + const QIcon& getIcon() const; void create(); void hide(); + void showMessage(const QString& pTitle, const QString& pMessage); + Q_SIGNALS: void fireShow(); void fireQuit(); diff --git a/src/ui/jsonapi/CMakeLists.txt b/src/ui/json/CMakeLists.txt similarity index 50% rename from src/ui/jsonapi/CMakeLists.txt rename to src/ui/json/CMakeLists.txt index 4dfe294..0e50bc4 100644 --- a/src/ui/jsonapi/CMakeLists.txt +++ b/src/ui/json/CMakeLists.txt @@ -1,12 +1,12 @@ ##################################################################### -# The jsonapi plugin implements the ui interface for internal usage. +# The json plugin implements the ui interface for internal usage. # # This plugin will be used by other ui plugins like AIDL or WebSocket # to provide the SDK functionality. It provides no possibility for # direct user control. ##################################################################### -ADD_PLATFORM_LIBRARY(AusweisAppUiJsonApi) +ADD_PLATFORM_LIBRARY(AusweisAppUiJson) -TARGET_LINK_LIBRARIES(AusweisAppUiJsonApi Qt5::Core AusweisAppCore AusweisAppGlobal AusweisAppActivationInternal) -TARGET_COMPILE_DEFINITIONS(AusweisAppUiJsonApi PRIVATE QT_STATICPLUGIN) +TARGET_LINK_LIBRARIES(AusweisAppUiJson Qt5::Core AusweisAppCore AusweisAppGlobal AusweisAppActivationInternal) +TARGET_COMPILE_DEFINITIONS(AusweisAppUiJson PRIVATE QT_STATICPLUGIN) diff --git a/src/ui/jsonapi/MessageDispatcher.cpp b/src/ui/json/MessageDispatcher.cpp similarity index 81% rename from src/ui/jsonapi/MessageDispatcher.cpp rename to src/ui/json/MessageDispatcher.cpp index 619f7ce..71d4694 100644 --- a/src/ui/jsonapi/MessageDispatcher.cpp +++ b/src/ui/json/MessageDispatcher.cpp @@ -16,15 +16,17 @@ #include "messages/MsgHandlerInsertCard.h" #include "messages/MsgHandlerInternalError.h" #include "messages/MsgHandlerInvalid.h" +#include "messages/MsgHandlerLog.h" #include "messages/MsgHandlerReader.h" #include "messages/MsgHandlerReaderList.h" #include "messages/MsgHandlerUnknownCommand.h" #include -Q_DECLARE_LOGGING_CATEGORY(jsonapi) +Q_DECLARE_LOGGING_CATEGORY(json) -#define HANDLE_CURRENT_STATE(msgType, msgHandler) handleCurrentState(requestType, msgType, [&] {return msgHandler;}); +#define HANDLE_CURRENT_STATE(msgType, msgHandler) handleCurrentState(requestType, msgType, [&] {return msgHandler;}) +#define HANDLE_INTERNAL_ONLY(msgHandler) handleInternalOnly(requestType, [&] {return msgHandler;}) using namespace governikus; @@ -35,12 +37,12 @@ MessageDispatcher::MessageDispatcher() } -QByteArray MessageDispatcher::init(const QSharedPointer& pContext) +QByteArray MessageDispatcher::init(const QSharedPointer& pWorkflowContext) { Q_ASSERT(!mContext.isActiveWorkflow()); reset(); - mContext.setWorkflowContext(pContext); + mContext.setWorkflowContext(pWorkflowContext); if (mContext.getAuthContext()) { @@ -82,7 +84,7 @@ QByteArray MessageDispatcher::processStateChange(const QString& pState) { if (!mContext.isActiveWorkflow() || pState.isEmpty()) { - qCCritical(jsonapi) << "Unexpected condition:" << mContext.getWorkflowContext() << "|" << pState; + qCCritical(json) << "Unexpected condition:" << mContext.getWorkflowContext() << '|' << pState; return MsgHandlerInternalError(QLatin1String("Unexpected condition")).getOutput(); } @@ -118,19 +120,19 @@ MsgHandler MessageDispatcher::createForStateChange(MsgType pStateType) } -QByteArray MessageDispatcher::processCommand(const QByteArray& pMsg) +MessageDispatcher::Msg MessageDispatcher::processCommand(const QByteArray& pMsg) { QJsonParseError jsonError; const auto& json = QJsonDocument::fromJson(pMsg, &jsonError); if (jsonError.error != QJsonParseError::NoError) { - return MsgHandlerInvalid(jsonError).getOutput(); + return MsgHandlerInvalid(jsonError); } const auto& obj = json.object(); auto msg = createForCommand(obj); msg.setRequest(obj); - return msg.getOutput(); + return msg; } @@ -143,7 +145,7 @@ MsgHandler MessageDispatcher::createForCommand(const QJsonObject& pObj) } auto requestType = Enum::fromString(cmd, MsgCmdType::UNDEFINED); - qCDebug(jsonapi) << "Process type:" << requestType; + qCDebug(json) << "Process type:" << requestType; switch (requestType) { case MsgCmdType::UNDEFINED: @@ -167,6 +169,9 @@ MsgHandler MessageDispatcher::createForCommand(const QJsonObject& pObj) case MsgCmdType::GET_READER_LIST: return MsgHandlerReaderList(); + case MsgCmdType::GET_LOG: + return HANDLE_INTERNAL_ONLY(MsgHandlerLog()); + case MsgCmdType::GET_INFO: return MsgHandlerInfo(); @@ -196,6 +201,20 @@ MsgHandler MessageDispatcher::createForCommand(const QJsonObject& pObj) } +MsgHandler MessageDispatcher::handleInternalOnly(MsgCmdType pCmdType, const std::function& pFunc) +{ +#ifdef QT_NO_DEBUG + Q_UNUSED(pFunc) + return MsgHandlerUnknownCommand(getEnumName(pCmdType)); + +#else + Q_UNUSED(pCmdType) + return pFunc(); + +#endif +} + + MsgHandler MessageDispatcher::handleCurrentState(MsgCmdType pCmdType, MsgType pMsgType, const std::function& pFunc) { if (mContext.getLastStateMsg() == pMsgType) @@ -229,3 +248,22 @@ MsgHandler MessageDispatcher::accept() return MsgHandlerBadState(MsgCmdType::ACCEPT); } + + +MessageDispatcher::Msg::Msg(const MsgHandler& pHandler) + : mType(pHandler.getType()) + , mData(pHandler.getOutput()) +{ +} + + +MessageDispatcher::Msg::operator QByteArray() const +{ + return mData; +} + + +MessageDispatcher::Msg::operator MsgType() const +{ + return mType; +} diff --git a/src/ui/jsonapi/MessageDispatcher.h b/src/ui/json/MessageDispatcher.h similarity index 68% rename from src/ui/jsonapi/MessageDispatcher.h rename to src/ui/json/MessageDispatcher.h index f05bdc2..bb25cbd 100644 --- a/src/ui/jsonapi/MessageDispatcher.h +++ b/src/ui/json/MessageDispatcher.h @@ -34,14 +34,28 @@ class MessageDispatcher MsgHandler cancel(); MsgHandler accept(); MsgHandler handleCurrentState(MsgCmdType pCmdType, MsgType pMsgType, const std::function& pFunc); + MsgHandler handleInternalOnly(MsgCmdType pCmdType, const std::function& pFunc); public: + class Msg final + { + friend class MessageDispatcher; + const MsgType mType; + const QByteArray mData; + + Msg(const MsgHandler& pHandler); + + public: + operator QByteArray() const; + operator MsgType() const; + }; + MessageDispatcher(); - QByteArray init(const QSharedPointer& pContext); + QByteArray init(const QSharedPointer& pWorkflowContext); QByteArray finish(); void reset(); - QByteArray processCommand(const QByteArray& pMsg); + Msg processCommand(const QByteArray& pMsg); QByteArray processStateChange(const QString& pState); QByteArray createMsgReader(const QString& pName) const; diff --git a/src/ui/jsonapi/UIPlugInJsonApi.cpp b/src/ui/json/UIPlugInJson.cpp similarity index 56% rename from src/ui/jsonapi/UIPlugInJsonApi.cpp rename to src/ui/json/UIPlugInJson.cpp index 7bd9316..3c87720 100644 --- a/src/ui/jsonapi/UIPlugInJsonApi.cpp +++ b/src/ui/json/UIPlugInJson.cpp @@ -2,18 +2,19 @@ * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany */ -#include "UIPlugInJsonApi.h" +#include "UIPlugInJson.h" +#include "messages/MsgTypes.h" #include "ReaderManager.h" #include #include -Q_DECLARE_LOGGING_CATEGORY(jsonapi) +Q_DECLARE_LOGGING_CATEGORY(json) using namespace governikus; -UIPlugInJsonApi::UIPlugInJsonApi() +UIPlugInJson::UIPlugInJson() : UIPlugIn() , mMessageDispatcher() , mEnabled(false) @@ -21,45 +22,48 @@ UIPlugInJsonApi::UIPlugInJsonApi() } -void UIPlugInJsonApi::setEnabled(bool pEnable) +void UIPlugInJson::setEnabled(bool pEnable) { mEnabled = pEnable; const auto readerManager = Env::getSingleton(); if (mEnabled) { - connect(readerManager, &ReaderManager::fireReaderAdded, this, &UIPlugInJsonApi::onReaderEvent); - connect(readerManager, &ReaderManager::fireReaderRemoved, this, &UIPlugInJsonApi::onReaderEvent); - connect(readerManager, &ReaderManager::fireCardInserted, this, &UIPlugInJsonApi::onReaderEvent); - connect(readerManager, &ReaderManager::fireCardRemoved, this, &UIPlugInJsonApi::onReaderEvent); + connect(readerManager, &ReaderManager::fireReaderAdded, this, &UIPlugInJson::onReaderEvent); + connect(readerManager, &ReaderManager::fireReaderRemoved, this, &UIPlugInJson::onReaderEvent); + connect(readerManager, &ReaderManager::fireCardInserted, this, &UIPlugInJson::onReaderEvent); + connect(readerManager, &ReaderManager::fireCardRemoved, this, &UIPlugInJson::onReaderEvent); } else { - disconnect(readerManager, &ReaderManager::fireReaderAdded, this, &UIPlugInJsonApi::onReaderEvent); - disconnect(readerManager, &ReaderManager::fireReaderRemoved, this, &UIPlugInJsonApi::onReaderEvent); - disconnect(readerManager, &ReaderManager::fireCardInserted, this, &UIPlugInJsonApi::onReaderEvent); - disconnect(readerManager, &ReaderManager::fireCardRemoved, this, &UIPlugInJsonApi::onReaderEvent); + disconnect(readerManager, &ReaderManager::fireReaderAdded, this, &UIPlugInJson::onReaderEvent); + disconnect(readerManager, &ReaderManager::fireReaderRemoved, this, &UIPlugInJson::onReaderEvent); + disconnect(readerManager, &ReaderManager::fireCardInserted, this, &UIPlugInJson::onReaderEvent); + disconnect(readerManager, &ReaderManager::fireCardRemoved, this, &UIPlugInJson::onReaderEvent); } } -bool UIPlugInJsonApi::isEnabled() const +bool UIPlugInJson::isEnabled() const { return mEnabled; } -void UIPlugInJsonApi::callFireMessage(const QByteArray& pMsg) +void UIPlugInJson::callFireMessage(const QByteArray& pMsg, bool pLogging) { if (!pMsg.isEmpty()) { - qCDebug(jsonapi).noquote() << "Fire message:" << pMsg; + if (Q_LIKELY(pLogging)) + { + qCDebug(json).noquote() << "Fire message:" << pMsg; + } Q_EMIT fireMessage(pMsg); } } -void UIPlugInJsonApi::onWorkflowStarted(QSharedPointer pContext) +void UIPlugInJson::onWorkflowStarted(QSharedPointer pContext) { if (!mEnabled) { @@ -68,14 +72,14 @@ void UIPlugInJsonApi::onWorkflowStarted(QSharedPointer pContext if (pContext.objectCast()) { - connect(pContext.data(), &WorkflowContext::fireStateChanged, this, &UIPlugInJsonApi::onStateChanged); + connect(pContext.data(), &WorkflowContext::fireStateChanged, this, &UIPlugInJson::onStateChanged); } callFireMessage(mMessageDispatcher.init(pContext)); } -void UIPlugInJsonApi::onWorkflowFinished(QSharedPointer ) +void UIPlugInJson::onWorkflowFinished(QSharedPointer ) { if (!mEnabled) { @@ -87,29 +91,30 @@ void UIPlugInJsonApi::onWorkflowFinished(QSharedPointer ) } -void UIPlugInJsonApi::onReaderEvent(const QString& pName) +void UIPlugInJson::onReaderEvent(const QString& pName) { callFireMessage(mMessageDispatcher.createMsgReader(pName)); } -void UIPlugInJsonApi::onStateChanged(const QString& pNewState) +void UIPlugInJson::onStateChanged(const QString& pNewState) { callFireMessage(mMessageDispatcher.processStateChange(pNewState)); } -void UIPlugInJsonApi::doMessageProcessing(const QByteArray& pMsg) +void UIPlugInJson::doMessageProcessing(const QByteArray& pMsg) { if (!mEnabled) { return; } - callFireMessage(mMessageDispatcher.processCommand(pMsg)); + const auto& msg = mMessageDispatcher.processCommand(pMsg); + callFireMessage(msg, msg != MsgType::LOG); } -void UIPlugInJsonApi::doShutdown() +void UIPlugInJson::doShutdown() { } diff --git a/src/ui/jsonapi/UIPlugInJsonApi.h b/src/ui/json/UIPlugInJson.h similarity index 86% rename from src/ui/jsonapi/UIPlugInJsonApi.h rename to src/ui/json/UIPlugInJson.h index 54c4b62..de8bb1c 100644 --- a/src/ui/jsonapi/UIPlugInJsonApi.h +++ b/src/ui/json/UIPlugInJson.h @@ -13,7 +13,7 @@ namespace governikus { -class UIPlugInJsonApi +class UIPlugInJson : public UIPlugIn { Q_OBJECT @@ -24,11 +24,11 @@ class UIPlugInJsonApi MessageDispatcher mMessageDispatcher; bool mEnabled; - inline void callFireMessage(const QByteArray& pMsg); + inline void callFireMessage(const QByteArray& pMsg, bool pLogging = true); public: - UIPlugInJsonApi(); - virtual ~UIPlugInJsonApi() override = default; + UIPlugInJson(); + virtual ~UIPlugInJson() override = default; void setEnabled(bool pEnable = true); bool isEnabled() const; diff --git a/src/ui/jsonapi/messages/MsgContext.cpp b/src/ui/json/messages/MsgContext.cpp similarity index 97% rename from src/ui/jsonapi/messages/MsgContext.cpp rename to src/ui/json/messages/MsgContext.cpp index 786ae93..908cf0c 100644 --- a/src/ui/jsonapi/messages/MsgContext.cpp +++ b/src/ui/json/messages/MsgContext.cpp @@ -8,8 +8,6 @@ #include -Q_DECLARE_LOGGING_CATEGORY(jsonapi) - using namespace governikus; diff --git a/src/ui/jsonapi/messages/MsgContext.h b/src/ui/json/messages/MsgContext.h similarity index 100% rename from src/ui/jsonapi/messages/MsgContext.h rename to src/ui/json/messages/MsgContext.h diff --git a/src/ui/jsonapi/messages/MsgHandler.cpp b/src/ui/json/messages/MsgHandler.cpp similarity index 88% rename from src/ui/jsonapi/messages/MsgHandler.cpp rename to src/ui/json/messages/MsgHandler.cpp index 8ece08e..4dd753f 100644 --- a/src/ui/jsonapi/messages/MsgHandler.cpp +++ b/src/ui/json/messages/MsgHandler.cpp @@ -64,14 +64,14 @@ MsgHandler::MsgHandler(MsgType pType) } -MsgHandler::MsgHandler(MsgType pType, const char* pKey, const QString& pValue) +MsgHandler::MsgHandler(MsgType pType, const char* const pKey, const QString& pValue) : MsgHandler(pType) { setValue(pKey, pValue); } -MsgHandler::MsgHandler(MsgType pType, const char* pKey, const QLatin1String pValue) +MsgHandler::MsgHandler(MsgType pType, const char* const pKey, const QLatin1String pValue) : MsgHandler(pType) { setValue(pKey, pValue); @@ -120,7 +120,7 @@ void MsgHandler::setRequest(const QJsonObject& pRequest) } -void MsgHandler::setValue(const char* pKey, const QString& pValue) +void MsgHandler::setValue(const char* const pKey, const QString& pValue) { setValue(QLatin1String(pKey), pValue); } @@ -135,7 +135,7 @@ void MsgHandler::setValue(const QLatin1String pKey, const QLatin1String pValue) } -void MsgHandler::setValue(const char* pKey, const QLatin1String pValue) +void MsgHandler::setValue(const char* const pKey, const QLatin1String pValue) { setValue(QLatin1String(pKey), pValue); } diff --git a/src/ui/jsonapi/messages/MsgHandler.h b/src/ui/json/messages/MsgHandler.h similarity index 75% rename from src/ui/jsonapi/messages/MsgHandler.h rename to src/ui/json/messages/MsgHandler.h index 4190250..bf879a2 100644 --- a/src/ui/jsonapi/messages/MsgHandler.h +++ b/src/ui/json/messages/MsgHandler.h @@ -24,14 +24,14 @@ class MsgHandler protected: QJsonObject mJsonObject; - MsgHandler(MsgType pType); - MsgHandler(MsgType pType, const char* pKey, const QString& pValue); - MsgHandler(MsgType pType, const char* pKey, const QLatin1String pValue); + explicit MsgHandler(MsgType pType); + explicit MsgHandler(MsgType pType, const char* const pKey, const QString& pValue); + explicit MsgHandler(MsgType pType, const char* const pKey, const QLatin1String pValue); void setValue(const QLatin1String pKey, const QString& pValue); - void setValue(const char* pKey, const QString& pValue); + void setValue(const char* const pKey, const QString& pValue); void setValue(const QLatin1String pKey, const QLatin1String pValue); - void setValue(const char* pKey, const QLatin1String pValue); + void setValue(const char* const pKey, const QLatin1String pValue); void setVoid(bool pVoid = true); diff --git a/src/ui/jsonapi/messages/MsgHandlerAccessRights.cpp b/src/ui/json/messages/MsgHandlerAccessRights.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerAccessRights.cpp rename to src/ui/json/messages/MsgHandlerAccessRights.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerAccessRights.h b/src/ui/json/messages/MsgHandlerAccessRights.h similarity index 83% rename from src/ui/jsonapi/messages/MsgHandlerAccessRights.h rename to src/ui/json/messages/MsgHandlerAccessRights.h index 206bdec..b1c50fc 100644 --- a/src/ui/jsonapi/messages/MsgHandlerAccessRights.h +++ b/src/ui/json/messages/MsgHandlerAccessRights.h @@ -27,8 +27,8 @@ class MsgHandlerAccessRights QJsonObject getAuxiliaryData(const QSharedPointer& pContext); public: - MsgHandlerAccessRights(const MsgContext& pContext); - MsgHandlerAccessRights(const QJsonObject& pObj, MsgContext& pContext); + explicit MsgHandlerAccessRights(const MsgContext& pContext); + explicit MsgHandlerAccessRights(const QJsonObject& pObj, MsgContext& pContext); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerApiLevel.cpp b/src/ui/json/messages/MsgHandlerApiLevel.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerApiLevel.cpp rename to src/ui/json/messages/MsgHandlerApiLevel.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerApiLevel.h b/src/ui/json/messages/MsgHandlerApiLevel.h similarity index 75% rename from src/ui/jsonapi/messages/MsgHandlerApiLevel.h rename to src/ui/json/messages/MsgHandlerApiLevel.h index f8b8ade..523420e 100644 --- a/src/ui/jsonapi/messages/MsgHandlerApiLevel.h +++ b/src/ui/json/messages/MsgHandlerApiLevel.h @@ -21,8 +21,8 @@ class MsgHandlerApiLevel void setAvailableLevel(); public: - MsgHandlerApiLevel(const MsgContext& pContext); - MsgHandlerApiLevel(const QJsonObject& pObj, MsgContext& pContext); + explicit MsgHandlerApiLevel(const MsgContext& pContext); + explicit MsgHandlerApiLevel(const QJsonObject& pObj, MsgContext& pContext); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerAuth.cpp b/src/ui/json/messages/MsgHandlerAuth.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerAuth.cpp rename to src/ui/json/messages/MsgHandlerAuth.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerAuth.h b/src/ui/json/messages/MsgHandlerAuth.h similarity index 78% rename from src/ui/jsonapi/messages/MsgHandlerAuth.h rename to src/ui/json/messages/MsgHandlerAuth.h index 988dbec..8724ca2 100644 --- a/src/ui/jsonapi/messages/MsgHandlerAuth.h +++ b/src/ui/json/messages/MsgHandlerAuth.h @@ -23,8 +23,8 @@ class MsgHandlerAuth public: MsgHandlerAuth(); - MsgHandlerAuth(const QJsonObject& pObj); - MsgHandlerAuth(const QSharedPointer& pContext); + explicit MsgHandlerAuth(const QJsonObject& pObj); + explicit MsgHandlerAuth(const QSharedPointer& pContext); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerBadState.cpp b/src/ui/json/messages/MsgHandlerBadState.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerBadState.cpp rename to src/ui/json/messages/MsgHandlerBadState.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerBadState.h b/src/ui/json/messages/MsgHandlerBadState.h similarity index 79% rename from src/ui/jsonapi/messages/MsgHandlerBadState.h rename to src/ui/json/messages/MsgHandlerBadState.h index b332887..446a787 100644 --- a/src/ui/jsonapi/messages/MsgHandlerBadState.h +++ b/src/ui/json/messages/MsgHandlerBadState.h @@ -15,7 +15,7 @@ class MsgHandlerBadState : public MsgHandler { public: - MsgHandlerBadState(MsgCmdType pType = MsgCmdType::UNDEFINED); + explicit MsgHandlerBadState(MsgCmdType pType = MsgCmdType::UNDEFINED); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerCertificate.cpp b/src/ui/json/messages/MsgHandlerCertificate.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerCertificate.cpp rename to src/ui/json/messages/MsgHandlerCertificate.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerCertificate.h b/src/ui/json/messages/MsgHandlerCertificate.h similarity index 83% rename from src/ui/jsonapi/messages/MsgHandlerCertificate.h rename to src/ui/json/messages/MsgHandlerCertificate.h index 4a4051e..a59b2ec 100644 --- a/src/ui/jsonapi/messages/MsgHandlerCertificate.h +++ b/src/ui/json/messages/MsgHandlerCertificate.h @@ -16,7 +16,7 @@ class MsgHandlerCertificate : public MsgHandler { public: - MsgHandlerCertificate(const MsgContext& pContext); + explicit MsgHandlerCertificate(const MsgContext& pContext); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterCan.cpp b/src/ui/json/messages/MsgHandlerEnterCan.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerEnterCan.cpp rename to src/ui/json/messages/MsgHandlerEnterCan.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterCan.h b/src/ui/json/messages/MsgHandlerEnterCan.h similarity index 70% rename from src/ui/jsonapi/messages/MsgHandlerEnterCan.h rename to src/ui/json/messages/MsgHandlerEnterCan.h index 9679d11..1cac03d 100644 --- a/src/ui/jsonapi/messages/MsgHandlerEnterCan.h +++ b/src/ui/json/messages/MsgHandlerEnterCan.h @@ -16,8 +16,8 @@ class MsgHandlerEnterCan : public MsgHandlerEnterNumber { public: - MsgHandlerEnterCan(const MsgContext& pContext); - MsgHandlerEnterCan(const QJsonObject& pObj, MsgContext& pContext); + explicit MsgHandlerEnterCan(const MsgContext& pContext); + explicit MsgHandlerEnterCan(const QJsonObject& pObj, MsgContext& pContext); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterNumber.cpp b/src/ui/json/messages/MsgHandlerEnterNumber.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerEnterNumber.cpp rename to src/ui/json/messages/MsgHandlerEnterNumber.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterNumber.h b/src/ui/json/messages/MsgHandlerEnterNumber.h similarity index 89% rename from src/ui/jsonapi/messages/MsgHandlerEnterNumber.h rename to src/ui/json/messages/MsgHandlerEnterNumber.h index 6aa5dc3..54d6e94 100644 --- a/src/ui/jsonapi/messages/MsgHandlerEnterNumber.h +++ b/src/ui/json/messages/MsgHandlerEnterNumber.h @@ -23,7 +23,7 @@ class MsgHandlerEnterNumber void setReader(const QSharedPointer& pContext); protected: - MsgHandlerEnterNumber(MsgType pType, const MsgContext& pContext); + explicit MsgHandlerEnterNumber(MsgType pType, const MsgContext& pContext); void parseValue(const QJsonObject& pObj, const MsgContext& pContext, diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterPin.cpp b/src/ui/json/messages/MsgHandlerEnterPin.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerEnterPin.cpp rename to src/ui/json/messages/MsgHandlerEnterPin.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterPin.h b/src/ui/json/messages/MsgHandlerEnterPin.h similarity index 70% rename from src/ui/jsonapi/messages/MsgHandlerEnterPin.h rename to src/ui/json/messages/MsgHandlerEnterPin.h index ee845d4..c1fe819 100644 --- a/src/ui/jsonapi/messages/MsgHandlerEnterPin.h +++ b/src/ui/json/messages/MsgHandlerEnterPin.h @@ -16,8 +16,8 @@ class MsgHandlerEnterPin : public MsgHandlerEnterNumber { public: - MsgHandlerEnterPin(const MsgContext& pContext); - MsgHandlerEnterPin(const QJsonObject& pObj, MsgContext& pContext); + explicit MsgHandlerEnterPin(const MsgContext& pContext); + explicit MsgHandlerEnterPin(const QJsonObject& pObj, MsgContext& pContext); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterPuk.cpp b/src/ui/json/messages/MsgHandlerEnterPuk.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerEnterPuk.cpp rename to src/ui/json/messages/MsgHandlerEnterPuk.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerEnterPuk.h b/src/ui/json/messages/MsgHandlerEnterPuk.h similarity index 70% rename from src/ui/jsonapi/messages/MsgHandlerEnterPuk.h rename to src/ui/json/messages/MsgHandlerEnterPuk.h index 8924e72..ce89f24 100644 --- a/src/ui/jsonapi/messages/MsgHandlerEnterPuk.h +++ b/src/ui/json/messages/MsgHandlerEnterPuk.h @@ -16,8 +16,8 @@ class MsgHandlerEnterPuk : public MsgHandlerEnterNumber { public: - MsgHandlerEnterPuk(const MsgContext& pContext); - MsgHandlerEnterPuk(const QJsonObject& pObj, MsgContext& pContext); + explicit MsgHandlerEnterPuk(const MsgContext& pContext); + explicit MsgHandlerEnterPuk(const QJsonObject& pObj, MsgContext& pContext); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerInfo.cpp b/src/ui/json/messages/MsgHandlerInfo.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerInfo.cpp rename to src/ui/json/messages/MsgHandlerInfo.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerInfo.h b/src/ui/json/messages/MsgHandlerInfo.h similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerInfo.h rename to src/ui/json/messages/MsgHandlerInfo.h diff --git a/src/ui/jsonapi/messages/MsgHandlerInsertCard.cpp b/src/ui/json/messages/MsgHandlerInsertCard.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerInsertCard.cpp rename to src/ui/json/messages/MsgHandlerInsertCard.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerInsertCard.h b/src/ui/json/messages/MsgHandlerInsertCard.h similarity index 84% rename from src/ui/jsonapi/messages/MsgHandlerInsertCard.h rename to src/ui/json/messages/MsgHandlerInsertCard.h index 8b7c85a..1afd725 100644 --- a/src/ui/jsonapi/messages/MsgHandlerInsertCard.h +++ b/src/ui/json/messages/MsgHandlerInsertCard.h @@ -16,7 +16,7 @@ class MsgHandlerInsertCard : public MsgHandler { public: - MsgHandlerInsertCard(MsgContext& pContext); + explicit MsgHandlerInsertCard(MsgContext& pContext); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerInternalError.cpp b/src/ui/json/messages/MsgHandlerInternalError.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerInternalError.cpp rename to src/ui/json/messages/MsgHandlerInternalError.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerInternalError.h b/src/ui/json/messages/MsgHandlerInternalError.h similarity index 67% rename from src/ui/jsonapi/messages/MsgHandlerInternalError.h rename to src/ui/json/messages/MsgHandlerInternalError.h index 513a336..396442a 100644 --- a/src/ui/jsonapi/messages/MsgHandlerInternalError.h +++ b/src/ui/json/messages/MsgHandlerInternalError.h @@ -15,8 +15,8 @@ class MsgHandlerInternalError : public MsgHandler { public: - MsgHandlerInternalError(const QString& pError = QString()); - MsgHandlerInternalError(const QLatin1String pError); + explicit MsgHandlerInternalError(const QString& pError = QString()); + explicit MsgHandlerInternalError(const QLatin1String pError); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerInvalid.cpp b/src/ui/json/messages/MsgHandlerInvalid.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerInvalid.cpp rename to src/ui/json/messages/MsgHandlerInvalid.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerInvalid.h b/src/ui/json/messages/MsgHandlerInvalid.h similarity index 62% rename from src/ui/jsonapi/messages/MsgHandlerInvalid.h rename to src/ui/json/messages/MsgHandlerInvalid.h index 49030c7..3fb022f 100644 --- a/src/ui/jsonapi/messages/MsgHandlerInvalid.h +++ b/src/ui/json/messages/MsgHandlerInvalid.h @@ -17,9 +17,9 @@ class MsgHandlerInvalid : public MsgHandler { public: - MsgHandlerInvalid(const QString& pError = QString()); - MsgHandlerInvalid(const QLatin1String pError); - MsgHandlerInvalid(const QJsonParseError& pError); + explicit MsgHandlerInvalid(const QString& pError = QString()); + explicit MsgHandlerInvalid(const QLatin1String pError); + explicit MsgHandlerInvalid(const QJsonParseError& pError); }; diff --git a/src/ui/json/messages/MsgHandlerLog.cpp b/src/ui/json/messages/MsgHandlerLog.cpp new file mode 100644 index 0000000..d0f9f40 --- /dev/null +++ b/src/ui/json/messages/MsgHandlerLog.cpp @@ -0,0 +1,17 @@ +/*! + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "MsgHandlerLog.h" + +#include "Env.h" +#include "LogHandler.h" + +using namespace governikus; + +MsgHandlerLog::MsgHandlerLog() + : MsgHandler(MsgType::LOG) +{ + const auto& data = Env::getSingleton()->getBacklog(); + mJsonObject[QLatin1String("data")] = QString::fromUtf8(data); +} diff --git a/src/ui/json/messages/MsgHandlerLog.h b/src/ui/json/messages/MsgHandlerLog.h new file mode 100644 index 0000000..ac6142c --- /dev/null +++ b/src/ui/json/messages/MsgHandlerLog.h @@ -0,0 +1,22 @@ +/*! + * \brief Message Log of JSON API. + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "MsgHandler.h" + +namespace governikus +{ + +class MsgHandlerLog + : public MsgHandler +{ + public: + MsgHandlerLog(); +}; + + +} // namespace governikus diff --git a/src/ui/jsonapi/messages/MsgHandlerReader.cpp b/src/ui/json/messages/MsgHandlerReader.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerReader.cpp rename to src/ui/json/messages/MsgHandlerReader.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerReader.h b/src/ui/json/messages/MsgHandlerReader.h similarity index 83% rename from src/ui/jsonapi/messages/MsgHandlerReader.h rename to src/ui/json/messages/MsgHandlerReader.h index 664ba9c..4919b2e 100644 --- a/src/ui/jsonapi/messages/MsgHandlerReader.h +++ b/src/ui/json/messages/MsgHandlerReader.h @@ -24,8 +24,8 @@ class MsgHandlerReader public: static QJsonObject createReaderInfo(const ReaderInfo& pInfo); - MsgHandlerReader(const QJsonObject& pObj); - MsgHandlerReader(const QString& pName); + explicit MsgHandlerReader(const QJsonObject& pObj); + explicit MsgHandlerReader(const QString& pName); }; diff --git a/src/ui/jsonapi/messages/MsgHandlerReaderList.cpp b/src/ui/json/messages/MsgHandlerReaderList.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerReaderList.cpp rename to src/ui/json/messages/MsgHandlerReaderList.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerReaderList.h b/src/ui/json/messages/MsgHandlerReaderList.h similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerReaderList.h rename to src/ui/json/messages/MsgHandlerReaderList.h diff --git a/src/ui/jsonapi/messages/MsgHandlerUnknownCommand.cpp b/src/ui/json/messages/MsgHandlerUnknownCommand.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgHandlerUnknownCommand.cpp rename to src/ui/json/messages/MsgHandlerUnknownCommand.cpp diff --git a/src/ui/jsonapi/messages/MsgHandlerUnknownCommand.h b/src/ui/json/messages/MsgHandlerUnknownCommand.h similarity index 83% rename from src/ui/jsonapi/messages/MsgHandlerUnknownCommand.h rename to src/ui/json/messages/MsgHandlerUnknownCommand.h index 6a7885e..ab78375 100644 --- a/src/ui/jsonapi/messages/MsgHandlerUnknownCommand.h +++ b/src/ui/json/messages/MsgHandlerUnknownCommand.h @@ -15,7 +15,7 @@ class MsgHandlerUnknownCommand : public MsgHandler { public: - MsgHandlerUnknownCommand(const QString& pName); + explicit MsgHandlerUnknownCommand(const QString& pName); }; diff --git a/src/ui/jsonapi/messages/MsgTypes.cpp b/src/ui/json/messages/MsgTypes.cpp similarity index 100% rename from src/ui/jsonapi/messages/MsgTypes.cpp rename to src/ui/json/messages/MsgTypes.cpp diff --git a/src/ui/jsonapi/messages/MsgTypes.h b/src/ui/json/messages/MsgTypes.h similarity index 97% rename from src/ui/jsonapi/messages/MsgTypes.h rename to src/ui/json/messages/MsgTypes.h index 33a39a6..0b3a04e 100644 --- a/src/ui/jsonapi/messages/MsgTypes.h +++ b/src/ui/json/messages/MsgTypes.h @@ -16,6 +16,7 @@ defineEnumType(MsgType, INVALID, UNKNOWN_COMMAND, INTERNAL_ERROR, + LOG, INFO, API_LEVEL, READER, @@ -33,6 +34,7 @@ defineEnumType(MsgCmdType, UNDEFINED, ACCEPT, CANCEL, + GET_LOG, GET_INFO, GET_API_LEVEL, SET_API_LEVEL, diff --git a/src/ui/json/metadata.json b/src/ui/json/metadata.json new file mode 100644 index 0000000..bc62536 --- /dev/null +++ b/src/ui/json/metadata.json @@ -0,0 +1,4 @@ +{ + "name" : "UIPlugInJson", + "dependencies" : [] +} diff --git a/src/ui/jsonapi/metadata.json b/src/ui/jsonapi/metadata.json deleted file mode 100644 index 040d7eb..0000000 --- a/src/ui/jsonapi/metadata.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name" : "UIPlugInJsonApi", - "dependencies" : [] -} diff --git a/src/ui/qml/AppUpdateDataModel.cpp b/src/ui/qml/AppUpdateDataModel.cpp new file mode 100644 index 0000000..365dca4 --- /dev/null +++ b/src/ui/qml/AppUpdateDataModel.cpp @@ -0,0 +1,79 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "AppUpdateDataModel.h" + +#include "Env.h" +#include "Service.h" +#include "SingletonHelper.h" + +using namespace governikus; + +defineSingleton(AppUpdateDataModel) + +AppUpdateDataModel::AppUpdateDataModel() + : QObject() + , mAppUpdateData(Env::getSingleton()->getUpdateData()) +{ +} + + +AppUpdateDataModel& AppUpdateDataModel::getInstance() +{ + return *Instance; +} + + +bool AppUpdateDataModel::isValid() const +{ + return mAppUpdateData.isValid(); +} + + +const QDateTime AppUpdateDataModel::getDate() const +{ + return mAppUpdateData.getDate(); +} + + +const QString& AppUpdateDataModel::getVersion() const +{ + return mAppUpdateData.getVersion(); +} + + +const QUrl& AppUpdateDataModel::getUrl() const +{ + return mAppUpdateData.getUrl(); +} + + +int AppUpdateDataModel::getSize() const +{ + return mAppUpdateData.getSize(); +} + + +const QUrl& AppUpdateDataModel::getChecksumUrl() const +{ + return mAppUpdateData.getChecksumUrl(); +} + + +const QUrl& AppUpdateDataModel::getNotesUrl() const +{ + return mAppUpdateData.getNotesUrl(); +} + + +const QString& AppUpdateDataModel::getNotes() const +{ + return mAppUpdateData.getNotes(); +} + + +void AppUpdateDataModel::onAppUpdateFinished(bool, const GlobalStatus&) +{ + mAppUpdateData = Env::getSingleton()->getUpdateData(); +} diff --git a/src/ui/qml/AppUpdateDataModel.h b/src/ui/qml/AppUpdateDataModel.h new file mode 100644 index 0000000..f98320a --- /dev/null +++ b/src/ui/qml/AppUpdateDataModel.h @@ -0,0 +1,53 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "AppUpdateData.h" +#include "Env.h" +#include "GlobalStatus.h" + + +namespace governikus +{ +class AppUpdateDataModel + : public QObject +{ + Q_OBJECT + friend class Env; + + Q_PROPERTY(bool valid READ isValid NOTIFY fireAppUpdateDataChanged) + Q_PROPERTY(QDateTime date READ getDate NOTIFY fireAppUpdateDataChanged) + Q_PROPERTY(QString version READ getVersion NOTIFY fireAppUpdateDataChanged) + Q_PROPERTY(QUrl url READ getUrl NOTIFY fireAppUpdateDataChanged) + Q_PROPERTY(int size READ getSize NOTIFY fireAppUpdateDataChanged) + Q_PROPERTY(QUrl checksumUrl READ getChecksumUrl NOTIFY fireAppUpdateDataChanged) + Q_PROPERTY(QUrl notesUrl READ getNotesUrl() NOTIFY fireAppUpdateDataChanged) + Q_PROPERTY(QString notes READ getNotes() NOTIFY fireAppUpdateDataChanged) + + private: + AppUpdateData mAppUpdateData; + + protected: + AppUpdateDataModel(); + static AppUpdateDataModel& getInstance(); + + public: + bool isValid() const; + const QDateTime getDate() const; + const QString& getVersion() const; + const QUrl& getUrl() const; + int getSize() const; + const QUrl& getChecksumUrl() const; + const QUrl& getNotesUrl() const; + const QString& getNotes() const; + + public Q_SLOTS: + void onAppUpdateFinished(bool, const GlobalStatus&); + + Q_SIGNALS: + void fireAppUpdateDataChanged(); +}; + +} // namespace governikus diff --git a/src/ui/qml/ApplicationModel.cpp b/src/ui/qml/ApplicationModel.cpp index 8b697f2..2100385 100644 --- a/src/ui/qml/ApplicationModel.cpp +++ b/src/ui/qml/ApplicationModel.cpp @@ -12,26 +12,35 @@ #include "BuildHelper.h" #include "DpiCalculator.h" #include "Env.h" +#include "HelpAction.h" #include "ReaderInfo.h" #include "ReaderManager.h" #include "RemoteClient.h" #include "SingletonHelper.h" -#if !defined(QT_NO_DEBUG) || defined(Q_OS_ANDROID) -#include "SurveyHandler.h" +#if !defined(Q_OS_WINRT) && !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +#include "PdfExporter.h" #endif +#include + #if (defined(Q_OS_LINUX) && !defined(QT_NO_DEBUG)) || defined(Q_OS_ANDROID) || defined(Q_OS_IOS) #include #endif #ifdef Q_OS_ANDROID +#include +#include +#include #include #endif using namespace governikus; +Q_DECLARE_LOGGING_CATEGORY(qml) +Q_DECLARE_LOGGING_CATEGORY(feedback) + defineSingleton(ApplicationModel) @@ -45,6 +54,7 @@ void ApplicationModel::onStatusChanged(const ReaderManagerPlugInInfo& pInfo) else if (pInfo.getPlugInType() == ReaderManagerPlugInType::NFC) { Q_EMIT fireNfcEnabledChanged(); + Q_EMIT fireNfcRunningChanged(); } } @@ -56,6 +66,12 @@ ApplicationModel::ApplicationModel() , mWifiInfo() , mWifiEnabled(false) , mBluetoothResponding(true) + , mFeedback() + , mFeedbackTimer() + , mFeedbackDisplayLength(3500) +#ifdef Q_OS_IOS + , mPrivate(new Private) +#endif { const auto readerManager = Env::getSingleton(); connect(readerManager, &ReaderManager::fireReaderAdded, this, &ApplicationModel::fireBluetoothReaderChanged); @@ -69,8 +85,11 @@ ApplicationModel::ApplicationModel() onWifiEnabledChanged(); - RemoteClient* const remoteClient = Env::getSingleton(); + auto* const remoteClient = Env::getSingleton(); connect(remoteClient, &RemoteClient::fireCertificateRemoved, this, &ApplicationModel::fireCertificateRemoved); + + mFeedbackTimer.setSingleShot(true); + connect(&mFeedbackTimer, &QTimer::timeout, this, &ApplicationModel::onShowNextFeedback, Qt::QueuedConnection); } @@ -84,6 +103,9 @@ void ApplicationModel::resetContext(const QSharedPointer& pCont if ((mContext = pContext)) { connect(mContext.data(), &WorkflowContext::fireReaderPlugInTypesChanged, this, &ApplicationModel::fireSelectedReaderChanged); + connect(mContext.data(), &WorkflowContext::fireReaderNameChanged, this, &ApplicationModel::fireSelectedReaderChanged); + connect(mContext.data(), &WorkflowContext::fireReaderNameChanged, this, &ApplicationModel::fireReaderPropertiesUpdated); + connect(mContext.data(), &WorkflowContext::fireReaderInfoChanged, this, &ApplicationModel::fireReaderPropertiesUpdated); } Q_EMIT fireCurrentWorkflowChanged(); } @@ -139,17 +161,51 @@ bool ApplicationModel::isNfcEnabled() const } +bool ApplicationModel::isNfcRunning() const +{ + return Env::getSingleton()->isScanRunning(ReaderManagerPlugInType::NFC); +} + + +void ApplicationModel::setNfcRunning(bool pRunning) +{ + const auto& readerManager = Env::getSingleton(); + if (pRunning) + { + readerManager->startScan(ReaderManagerPlugInType::NFC); + return; + } + + readerManager->stopScan(ReaderManagerPlugInType::NFC); +} + + bool ApplicationModel::isExtendedLengthApdusUnsupported() const { - if (mContext && !mContext->getReaderName().isEmpty()) + + if (mContext) { - ReaderInfo readerInfo = Env::getSingleton()->getReaderInfo(mContext->getReaderName()); - return !readerInfo.sufficientApduLength(); + if (mContext->currentReaderHasEidCardButInsufficientApduLength()) + { + return true; + } + if (!mContext->getReaderName().isEmpty()) + { + ReaderInfo readerInfo = Env::getSingleton()->getReaderInfo(mContext->getReaderName()); + return !readerInfo.sufficientApduLength(); + } } return false; } +void ApplicationModel::stopNfcScanWithError(const QString& pError) const +{ + const auto readerManager = Env::getSingleton(); + readerManager->stopScan(ReaderManagerPlugInType::NFC, pError); +} + + bool ApplicationModel::isBluetoothAvailable() const { return getFirstPlugInInfo(ReaderManagerPlugInType::BLUETOOTH).isAvailable(); @@ -201,6 +257,18 @@ bool ApplicationModel::locationPermissionRequired() const } +bool ApplicationModel::isWifiEnabled() const +{ + return mWifiEnabled; +} + + +qreal ApplicationModel::getDpiScale() const +{ + return mDpiScale; +} + + qreal ApplicationModel::getScaleFactor() const { return mScaleFactor; @@ -244,33 +312,212 @@ bool ApplicationModel::foundSelectedReader() const return false; } - return Env::getSingleton()->getReaderInfos(mContext->getReaderPlugInTypes()).size() > 0; + return !Env::getSingleton()->getReaderInfos(ReaderFilter(mContext->getReaderPlugInTypes())).isEmpty(); } -bool ApplicationModel::areStoreFeedbackDialogConditionsMet() const +bool ApplicationModel::isReaderTypeAvailable(ReaderManagerPlugInType pPlugInType) const { - if (!getCurrentWorkflow().isEmpty()) + if (!mContext) { return false; } -#ifdef Q_OS_ANDROID - const bool startedByAuth = QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/MainActivity", "isStartedByAuth"); - if (startedByAuth) + if (!mContext->getReaderName().isEmpty()) { - return false; + return Env::getSingleton()->getReaderInfo(mContext->getReaderName()).getPlugInType() == pPlugInType; } + + return !Env::getSingleton()->getReaderInfos(ReaderFilter({pPlugInType})).isEmpty(); +} + + +void ApplicationModel::showSettings(const ApplicationModel::Settings& pAction) +{ +#ifdef Q_OS_ANDROID + const auto& androidQ = QOperatingSystemVersion(QOperatingSystemVersion::Android, 10); + + switch (pAction) + { + case Settings::SETTING_NETWORK: + if (QOperatingSystemVersion::current() >= androidQ) + { + showSettings(QStringLiteral("android.settings.panel.action.INTERNET_CONNECTIVITY")); + } + else + { + showSettings(QStringLiteral("android.settings.WIRELESS_SETTINGS")); + } + break; + + case Settings::SETTING_NFC: + if (QOperatingSystemVersion::current() >= androidQ) + { + showSettings(QStringLiteral("android.settings.panel.action.NFC")); + } + else + { + showSettings(QStringLiteral("android.settings.NFC_SETTINGS")); + } + + break; + + case Settings::SETTING_BLUETOOTH: + showSettings(QStringLiteral("android.settings.BLUETOOTH_SETTINGS")); + break; + } +#else + qCWarning(qml) << "NOT IMPLEMENTED:" << pAction; +#endif +} + + +void ApplicationModel::showSettings(const QString& pAction) +{ +#ifdef Q_OS_ANDROID + QAndroidJniEnvironment env; + + const QAndroidJniObject& jAction = QAndroidJniObject::fromString(pAction); + QAndroidJniObject intent("android/content/Intent", "(Ljava/lang/String;)V", jAction.object()); + const jint flag = QAndroidJniObject::getStaticField("android/content/Intent", "FLAG_ACTIVITY_NEW_TASK"); + intent.callObjectMethod("setFlags", "(I)V", flag); + + if (intent.isValid()) + { + qCCritical(qml) << "Call action:" << pAction; + QtAndroid::startActivity(intent, 0); + } + + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Cannot call an action as activity:" << pAction; + env->ExceptionDescribe(); + env->ExceptionClear(); + } +#else + qCWarning(qml) << "NOT IMPLEMENTED:" << pAction; +#endif +} + + +#ifndef Q_OS_IOS +bool ApplicationModel::isScreenReaderRunning() const +{ +#ifdef Q_OS_ANDROID + const jboolean result = QtAndroid::androidActivity().callMethod("isScreenReaderRunning", "()Z"); + return result != JNI_FALSE; + +#else + qCWarning(qml) << "NOT IMPLEMENTED"; + return false; + +#endif +} + + #endif - const auto& settings = Env::getSingleton()->getGeneralSettings(); - return settings.isRequestStoreFeedback(); +QString ApplicationModel::getFeedback() const +{ + return mFeedback.isEmpty() ? QString() : mFeedback.first(); } -void ApplicationModel::hideFutureStoreFeedbackDialogs() +void ApplicationModel::onShowNextFeedback() { - Env::getSingleton()->getGeneralSettings().setRequestStoreFeedback(false); + mFeedback.removeFirst(); + if (!mFeedback.isEmpty() && !isScreenReaderRunning()) + { + mFeedbackTimer.start(mFeedbackDisplayLength); + } + + Q_EMIT fireFeedbackChanged(); +} + + +void ApplicationModel::showFeedback(const QString& pMessage, bool pReplaceExisting) +{ + qCInfo(feedback).noquote() << pMessage; + +#if defined(Q_OS_ANDROID) + Q_UNUSED(pReplaceExisting) + // Wait for toast activation synchronously so that the app can not be deactivated + // in the meantime and all used Java objects are still alive when accessed. + QtAndroid::runOnAndroidThreadSync([pMessage](){ + QAndroidJniEnvironment env; + + const QAndroidJniObject& jMessage = QAndroidJniObject::fromString(pMessage); + const QAndroidJniObject& toast = QAndroidJniObject::callStaticObjectMethod( + "android/widget/Toast", + "makeText", + "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;", + QtAndroid::androidActivity().object(), + jMessage.object(), + jint(1)); + toast.callMethod("show"); + + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Suppressing an unexpected exception."; + env->ExceptionDescribe(); + env->ExceptionClear(); + // The toast was probably not displayed (e.g. DeadObjectException). We halt on error + // since it is used to display information to the user as required by the TR. + Q_ASSERT(false); + } + }); +#else + if (pReplaceExisting) + { + mFeedbackTimer.stop(); + mFeedback.clear(); + } + + const bool initial = mFeedback.isEmpty(); + mFeedback << pMessage; + if (initial) + { + if (!isScreenReaderRunning()) + { + mFeedbackTimer.start(mFeedbackDisplayLength); + } + Q_EMIT fireFeedbackChanged(); + } +#endif +} + + +#ifndef Q_OS_IOS +void ApplicationModel::keepScreenOn(bool pActive) +{ +#if defined(Q_OS_ANDROID) + QtAndroid::runOnAndroidThread([pActive](){ + QtAndroid::androidActivity().callMethod("keepScreenOn", "(Z)V", pActive); + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) + { + qCCritical(qml) << "Exception calling java native function."; + env->ExceptionDescribe(); + env->ExceptionClear(); + } + }); + +#else + qCWarning(qml) << "NOT IMPLEMENTED:" << pActive; +#endif +} + + +#endif + + +void ApplicationModel::openOnlineHelp(const QString& pHelpSectionName) +{ +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + qCWarning(qml) << "NOT IMPLEMENTED:" << pHelpSectionName; +#else + HelpAction::openContextHelp(pHelpSectionName); +#endif } @@ -291,3 +538,9 @@ ApplicationModel& ApplicationModel::getInstance() { return *Instance; } + + +QString ApplicationModel::stripHtmlTags(QString pString) const +{ + return pString.remove(QRegularExpression(QStringLiteral("<[^>]*>"))); +} diff --git a/src/ui/qml/ApplicationModel.h b/src/ui/qml/ApplicationModel.h index 45677c0..e1e1382 100644 --- a/src/ui/qml/ApplicationModel.h +++ b/src/ui/qml/ApplicationModel.h @@ -7,6 +7,7 @@ #pragma once #include "context/WorkflowContext.h" +#include "Env.h" #include "ReaderInfo.h" #include "ReaderManagerPlugInInfo.h" #include "WifiInfo.h" @@ -14,7 +15,12 @@ #include #include #include -#include +#include +#include + +#ifdef Q_OS_IOS +Q_FORWARD_DECLARE_OBJC_CLASS(VoiceOverObserver); +#endif namespace governikus { @@ -23,11 +29,13 @@ class ApplicationModel : public QObject { Q_OBJECT + friend class Env; Q_PROPERTY(QString packageName READ getPackageName CONSTANT) Q_PROPERTY(bool nfcEnabled READ isNfcEnabled NOTIFY fireNfcEnabledChanged) Q_PROPERTY(bool nfcAvailable READ isNfcAvailable CONSTANT) + Q_PROPERTY(bool nfcRunning READ isNfcRunning WRITE setNfcRunning NOTIFY fireNfcRunningChanged) Q_PROPERTY(bool extendedLengthApdusUnsupported READ isExtendedLengthApdusUnsupported NOTIFY fireReaderPropertiesUpdated) Q_PROPERTY(bool bluetoothEnabled READ isBluetoothEnabled WRITE setBluetoothEnabled NOTIFY fireBluetoothEnabledChanged) @@ -35,12 +43,15 @@ class ApplicationModel Q_PROPERTY(bool bluetoothAvailable READ isBluetoothAvailable CONSTANT) Q_PROPERTY(bool locationPermissionRequired READ locationPermissionRequired NOTIFY fireBluetoothReaderChanged) + Q_PROPERTY(qreal dpiScale READ getDpiScale CONSTANT) Q_PROPERTY(qreal scaleFactor READ getScaleFactor WRITE setScaleFactor NOTIFY fireScaleFactorChanged) - Q_PROPERTY(bool wifiEnabled MEMBER mWifiEnabled NOTIFY fireWifiEnabledChanged) + Q_PROPERTY(bool wifiEnabled READ isWifiEnabled NOTIFY fireWifiEnabledChanged) Q_PROPERTY(QString currentWorkflow READ getCurrentWorkflow NOTIFY fireCurrentWorkflowChanged) Q_PROPERTY(bool foundSelectedReader READ foundSelectedReader NOTIFY fireSelectedReaderChanged) + Q_PROPERTY(QString feedback READ getFeedback NOTIFY fireFeedbackChanged) + QSharedPointer mContext; void onStatusChanged(const ReaderManagerPlugInInfo& pInfo); @@ -52,6 +63,18 @@ class ApplicationModel WifiInfo mWifiInfo; bool mWifiEnabled; bool mBluetoothResponding; + QStringList mFeedback; + QTimer mFeedbackTimer; + const int mFeedbackDisplayLength; +#ifdef Q_OS_IOS + struct Private + { + Private(); + ~Private(); + VoiceOverObserver* const mObserver; + }; + const QScopedPointer mPrivate; +#endif private Q_SLOTS: void onWifiEnabledChanged(); @@ -59,14 +82,25 @@ class ApplicationModel protected: ApplicationModel(); ~ApplicationModel() override = default; + static ApplicationModel& getInstance(); public: + enum class Settings + { + SETTING_NETWORK, + SETTING_NFC, + SETTING_BLUETOOTH + }; + Q_ENUM(Settings) + void resetContext(const QSharedPointer& pContext = QSharedPointer()); QString getPackageName() const; bool isNfcAvailable() const; bool isNfcEnabled() const; + bool isNfcRunning() const; + void setNfcRunning(bool pRunning); bool isExtendedLengthApdusUnsupported() const; bool isBluetoothAvailable() const; @@ -75,20 +109,40 @@ class ApplicationModel void setBluetoothEnabled(bool pEnabled); bool locationPermissionRequired() const; + bool isWifiEnabled() const; + qreal getDpiScale() const; qreal getScaleFactor() const; void setScaleFactor(qreal pScaleFactor); QString getCurrentWorkflow() const; bool foundSelectedReader() const; - Q_INVOKABLE void enableWifi(); - Q_INVOKABLE bool areStoreFeedbackDialogConditionsMet() const; - Q_INVOKABLE void hideFutureStoreFeedbackDialogs(); + QString getFeedback() const; - static ApplicationModel& getInstance(); + Q_INVOKABLE bool isScreenReaderRunning() const; + + Q_INVOKABLE void stopNfcScanWithError(const QString& pError) const; + + Q_INVOKABLE bool isReaderTypeAvailable(ReaderManagerPlugInType pPlugInType) const; + + Q_INVOKABLE void enableWifi(); + + Q_INVOKABLE void showSettings(const Settings& pAction); + Q_INVOKABLE void showSettings(const QString& pAction); + Q_INVOKABLE void showFeedback(const QString& pMessage, bool pReplaceExisting = false); + Q_INVOKABLE void keepScreenOn(bool pActive); + Q_INVOKABLE void openOnlineHelp(const QString& pHelpSectionName); + Q_INVOKABLE QString stripHtmlTags(QString pString) const; +#ifdef Q_OS_IOS + Q_INVOKABLE void showAppStoreRatingDialog(); +#endif + + public Q_SLOTS: + Q_INVOKABLE void onShowNextFeedback(); Q_SIGNALS: void fireNfcEnabledChanged(); + void fireNfcRunningChanged(); void fireReaderPropertiesUpdated(); void fireBluetoothEnabledChanged(); @@ -100,7 +154,9 @@ class ApplicationModel void fireScaleFactorChanged(); void fireWifiEnabledChanged(); - void fireCertificateRemoved(QString pDeviceName); + void fireCertificateRemoved(const QString& pDeviceName); + + void fireFeedbackChanged(); }; diff --git a/src/ui/qml/ApplicationModel_ios.mm b/src/ui/qml/ApplicationModel_ios.mm new file mode 100644 index 0000000..59da1d1 --- /dev/null +++ b/src/ui/qml/ApplicationModel_ios.mm @@ -0,0 +1,98 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "ApplicationModel.h" + +#include + +#import +#import + +Q_DECLARE_LOGGING_CATEGORY(feedback) + +using namespace governikus; + +@interface VoiceOverObserver + : NSObject +@property BOOL mRunning; +- (instancetype) init; +- (void) dealloc; +- (void) receiveNotification: (NSNotification*) notification; +@end + +@implementation VoiceOverObserver + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + + +- (instancetype)init { + self = [super init]; + if (!self) + { + return nil; + } + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(receiveNotification:) + name:UIAccessibilityVoiceOverStatusDidChangeNotification + object:nil]; + + self.mRunning = UIAccessibilityIsVoiceOverRunning(); + + return self; +} + + +- (void)receiveNotification:(NSNotification*)notification { + if ([notification.name + isEqualToString: + UIAccessibilityVoiceOverStatusDidChangeNotification]) + { + self.mRunning = UIAccessibilityIsVoiceOverRunning(); + } +} + + +@end + + +ApplicationModel::Private::Private() : mObserver([[VoiceOverObserver alloc] init]) +{ +} + + +ApplicationModel::Private::~Private() +{ + [mObserver dealloc]; +} + + +bool ApplicationModel::isScreenReaderRunning() const +{ + return mPrivate->mObserver.mRunning; +} + + +void ApplicationModel::keepScreenOn(bool pActive) +{ + if (pActive) + { + [[UIApplication sharedApplication]setIdleTimerDisabled:YES]; + } + else + { + [[UIApplication sharedApplication]setIdleTimerDisabled:NO]; + } +} + + +void ApplicationModel::showAppStoreRatingDialog() +{ + qCDebug(feedback) << "Requesting iOS AppStore review"; + [SKStoreReviewController requestReview]; +} diff --git a/src/ui/qml/AuthModel.cpp b/src/ui/qml/AuthModel.cpp index 6fe0142..bd1530f 100644 --- a/src/ui/qml/AuthModel.cpp +++ b/src/ui/qml/AuthModel.cpp @@ -4,7 +4,6 @@ #include "AuthModel.h" -#include "Env.h" #include "ReaderManagerPlugInInfo.h" #include "SingletonHelper.h" @@ -29,6 +28,7 @@ void AuthModel::resetContext(const QSharedPointer& pContext) if (mContext) { connect(mContext.data(), &AuthContext::fireDidAuthenticateEac1Changed, this, &AuthModel::onDidAuthenticateEac1Changed); + connect(mContext.data(), &AuthContext::fireProgressChanged, this, &AuthModel::fireProgressChanged); } if (!mTransactionInfo.isEmpty()) @@ -37,6 +37,8 @@ void AuthModel::resetContext(const QSharedPointer& pContext) Q_EMIT fireTransactionInfoChanged(); } + + Q_EMIT fireProgressChanged(); } @@ -46,6 +48,28 @@ const QString& AuthModel::getTransactionInfo() const } +int AuthModel::getProgressValue() const +{ + if (mContext) + { + return mContext->getProgressValue(); + } + + return 0; +} + + +const QString AuthModel::getProgressMessage() const +{ + if (mContext) + { + return mContext->getProgressMessage(); + } + + return QString(); +} + + AuthModel& AuthModel::getInstance() { return *Instance; diff --git a/src/ui/qml/AuthModel.h b/src/ui/qml/AuthModel.h index 83e125e..c218c43 100644 --- a/src/ui/qml/AuthModel.h +++ b/src/ui/qml/AuthModel.h @@ -7,6 +7,7 @@ #pragma once #include "context/AuthContext.h" +#include "Env.h" #include "WorkflowModel.h" #include @@ -14,8 +15,6 @@ #include #include -class test_AuthModel; - namespace governikus { @@ -23,32 +22,36 @@ class AuthModel : public WorkflowModel { Q_OBJECT + friend class Env; Q_PROPERTY(QString transactionInfo READ getTransactionInfo NOTIFY fireTransactionInfoChanged) + Q_PROPERTY(int progressValue READ getProgressValue NOTIFY fireProgressChanged) + Q_PROPERTY(QString progressMessage READ getProgressMessage NOTIFY fireProgressChanged) private: - friend class ::test_AuthModel; QSharedPointer mContext; QString mTransactionInfo; protected: AuthModel(); ~AuthModel() override = default; + static AuthModel& getInstance(); public: void resetContext(const QSharedPointer& pContext = QSharedPointer()); const QString& getTransactionInfo() const; - - static AuthModel& getInstance(); + int getProgressValue() const; + const QString getProgressMessage() const; Q_INVOKABLE void setSkipRedirect(bool pSkipRedirect); - public Q_SLOTS: + private Q_SLOTS: void onDidAuthenticateEac1Changed(); Q_SIGNALS: void fireTransactionInfoChanged(); + void fireProgressChanged(); }; diff --git a/src/ui/qml/CMakeLists.txt b/src/ui/qml/CMakeLists.txt index f767236..98087cb 100644 --- a/src/ui/qml/CMakeLists.txt +++ b/src/ui/qml/CMakeLists.txt @@ -4,7 +4,8 @@ ADD_PLATFORM_LIBRARY(AusweisAppUiQml) -TARGET_LINK_LIBRARIES(AusweisAppUiQml Qt5::Core Qt5::Svg Qt5::Qml Qt5::Quick Qt5::QuickControls2 AusweisAppGlobal AusweisAppCore AusweisAppUi AusweisAppRemoteDevice AusweisAppUiCommon) +TARGET_LINK_LIBRARIES(AusweisAppUiQml Qt5::Core Qt5::Svg Qt5::Qml Qt5::Quick Qt5::QuickControls2) +TARGET_LINK_LIBRARIES(AusweisAppUiQml AusweisAppGlobal AusweisAppCore AusweisAppUi AusweisAppRemoteDevice AusweisAppUiCommon AusweisAppExport) IF(ANDROID) TARGET_LINK_LIBRARIES(AusweisAppUiQml AusweisAppWhitelistClient) @@ -12,10 +13,6 @@ ELSE() TARGET_LINK_LIBRARIES(AusweisAppUiQml debug AusweisAppWhitelistClient) ENDIF() -IF(DESKTOP) - TARGET_LINK_LIBRARIES(AusweisAppUiQml AusweisAppExport) -ENDIF() - IF(TARGET Qt5::Bluetooth) TARGET_LINK_LIBRARIES(AusweisAppUiQml Qt5::Bluetooth) ENDIF() diff --git a/src/ui/qml/CertificateDescriptionModel.cpp b/src/ui/qml/CertificateDescriptionModel.cpp index 0a3686b..8784bff 100644 --- a/src/ui/qml/CertificateDescriptionModel.cpp +++ b/src/ui/qml/CertificateDescriptionModel.cpp @@ -44,21 +44,28 @@ void CertificateDescriptionModel::initModelData(const QSharedPointergetTermsOfUsage().remove(QLatin1Char('\r')).replace(QLatin1Char('\t'), QLatin1Char(' ')); const bool showDetailedProviderInfo = !(serviceProviderAddress.isEmpty() || purpose.isEmpty() || dataSecurityOfficer.isEmpty()); + //: LABEL ALL_PLATFORMS mData += QPair(tr("Service provider"), getSubjectName() + QLatin1Char('\n') + getSubjectUrl()); + //: LABEL ALL_PLATFORMS mData += QPair(tr("Certificate issuer"), pCertDescription->getIssuerName() + QLatin1Char('\n') + pCertDescription->getIssuerUrl()); if (showDetailedProviderInfo) { + //: LABEL ALL_PLATFORMS mData += QPair(tr("Name, address and mail address of the service provider"), serviceProviderAddress); + //: LABEL ALL_PLATFORMS mData += QPair(tr("Purpose"), purpose); + //: LABEL ALL_PLATFORMS mData += QPair(tr("Indication of the bodies responsible for the service provider, " "that verify the compliance with data security regulations"), dataSecurityOfficer); } else if (!termsOfUsage.isEmpty()) { + //: LABEL ALL_PLATFORMS mData += QPair(tr("Service provider information"), termsOfUsage); } if (!getValidity().isEmpty()) { + //: LABEL ALL_PLATFORMS mData += QPair(tr("Validity"), getValidity()); } } diff --git a/src/ui/qml/CertificateDescriptionModel.h b/src/ui/qml/CertificateDescriptionModel.h index 1cbce36..8c66b76 100644 --- a/src/ui/qml/CertificateDescriptionModel.h +++ b/src/ui/qml/CertificateDescriptionModel.h @@ -28,13 +28,6 @@ class CertificateDescriptionModel QVector > mData; QSharedPointer mContext; - enum UserRoles - { - LABEL = Qt::UserRole + 1, - TEXT - }; - - inline QSharedPointer getCertificateDescription() const; inline QString getValidity() const; void initModelData(const QSharedPointer& pCertDescription); @@ -43,6 +36,12 @@ class CertificateDescriptionModel void onDidAuthenticateEac1Changed(); public: + enum UserRoles + { + LABEL = Qt::UserRole + 1, + TEXT + }; + CertificateDescriptionModel(QObject* pParent = nullptr); void resetContext(const QSharedPointer& pContext = QSharedPointer()); diff --git a/src/ui/qml/ChangePinModel.cpp b/src/ui/qml/ChangePinModel.cpp index 9c8ac6f..0600af1 100644 --- a/src/ui/qml/ChangePinModel.cpp +++ b/src/ui/qml/ChangePinModel.cpp @@ -4,12 +4,12 @@ #include "ChangePinModel.h" -#include "Env.h" #include "ReaderManager.h" #include "SingletonHelper.h" using namespace governikus; + defineSingleton(ChangePinModel) @@ -21,6 +21,7 @@ void ChangePinModel::resetContext(const QSharedPointer& pConte if (mContext) { connect(mContext.data(), &ChangePinContext::fireSuccessMessageChanged, this, &WorkflowModel::fireResultChanged); + connect(mContext.data(), &ChangePinContext::firePaceResultUpdated, this, &ChangePinModel::onPaceResultUpdated); Q_EMIT fireNewContextSet(); } @@ -42,3 +43,12 @@ ChangePinModel& ChangePinModel::getInstance() { return *Instance; } + + +void ChangePinModel::onPaceResultUpdated() +{ + if (mContext->getLastPaceResult() == CardReturnCode::OK_PUK) + { + Q_EMIT fireOnPinUnlocked(); + } +} diff --git a/src/ui/qml/ChangePinModel.h b/src/ui/qml/ChangePinModel.h index 5ccf805..ad828ce 100644 --- a/src/ui/qml/ChangePinModel.h +++ b/src/ui/qml/ChangePinModel.h @@ -7,6 +7,7 @@ #pragma once #include "context/ChangePinContext.h" +#include "Env.h" #include "WorkflowModel.h" #include @@ -14,8 +15,6 @@ #include #include -class test_ChangePinModel; - namespace governikus { @@ -23,24 +22,27 @@ class ChangePinModel : public WorkflowModel { Q_OBJECT + friend class Env; private: - friend class ::test_ChangePinModel; QSharedPointer mContext; protected: ChangePinModel() = default; ~ChangePinModel() override = default; + static ChangePinModel& getInstance(); public: void resetContext(const QSharedPointer& pContext = QSharedPointer()); virtual QString getResultString() const override; - static ChangePinModel& getInstance(); + private Q_SLOTS: + void onPaceResultUpdated(); Q_SIGNALS: void fireNewContextSet(); + void fireOnPinUnlocked(); }; diff --git a/src/ui/qml/ConnectivityManager.cpp b/src/ui/qml/ConnectivityManager.cpp index d8f2b2b..041b63f 100644 --- a/src/ui/qml/ConnectivityManager.cpp +++ b/src/ui/qml/ConnectivityManager.cpp @@ -25,7 +25,7 @@ ConnectivityManager::ConnectivityManager(QObject* pParent) ConnectivityManager::~ConnectivityManager() { - if (mTimerId) + if (mTimerId != 0) { killTimer(mTimerId); } @@ -105,7 +105,7 @@ bool ConnectivityManager::isNetworkInterfaceActive() const void ConnectivityManager::startWatching() { - if (mTimerId) + if (mTimerId != 0) { qCWarning(network) << "Already started, skip"; return; @@ -117,7 +117,7 @@ void ConnectivityManager::startWatching() void ConnectivityManager::stopWatching() { - if (!mTimerId) + if (mTimerId == 0) { qCWarning(network) << "Already stopped, skip"; return; diff --git a/src/ui/qml/DpiCalculator_generic.cpp b/src/ui/qml/DpiCalculator.cpp similarity index 50% rename from src/ui/qml/DpiCalculator_generic.cpp rename to src/ui/qml/DpiCalculator.cpp index a341848..61707f6 100644 --- a/src/ui/qml/DpiCalculator_generic.cpp +++ b/src/ui/qml/DpiCalculator.cpp @@ -22,13 +22,30 @@ qreal DpiCalculator::getDpi() auto screen = app->primaryScreen(); if (screen) { +#if defined(Q_OS_ANDROID) + auto envDpi = qgetenv("QT_ANDROID_THEME_DISPLAY_DPI"); + bool ok; + dpi = envDpi.toDouble(&ok); + if (!ok) + { + qCCritical(qml) << "Cannot get screen dpi"; + dpi = -1.0; + } +#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) + // The physical DPI changes on Apple devices with screen scaling, on other platforms it behaves the other way around. + dpi = screen->physicalDotsPerInch(); +#else dpi = screen->logicalDotsPerInch(); +#endif + qCInfo(qml) << "Determined dpi:" + << screen->logicalDotsPerInch() << "(logical)," + << screen->physicalDotsPerInch() << "(physical)"; } - qCInfo(qml) << "Determined dpi" << dpi; + qCInfo(qml) << "Using dpi" << dpi; #ifndef QT_NO_DEBUG - const char* overrideDpi = "OVERRIDE_DPI"; + const char* const overrideDpi = "OVERRIDE_DPI"; if (!qEnvironmentVariableIsEmpty(overrideDpi)) { dpi = qEnvironmentVariableIntValue(overrideDpi); diff --git a/src/ui/qml/DpiCalculator_android.cpp b/src/ui/qml/DpiCalculator_android.cpp deleted file mode 100644 index e5e02f9..0000000 --- a/src/ui/qml/DpiCalculator_android.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "DpiCalculator.h" - -#include -#include - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(qml) - - -qreal DpiCalculator::getDpi() -{ - auto qtDpi = qgetenv("QT_ANDROID_THEME_DISPLAY_DPI"); - bool ok; - qreal dpi = qtDpi.toDouble(&ok); - if (!ok) - { - qCCritical(qml) << "Cannot get screen dpi"; - return -1.0; - } - - qCInfo(qml) << "Determined dpi" << dpi; - - return dpi; -} diff --git a/src/ui/qml/DpiCalculator_ios.mm b/src/ui/qml/DpiCalculator_ios.mm deleted file mode 100644 index ad907a4..0000000 --- a/src/ui/qml/DpiCalculator_ios.mm +++ /dev/null @@ -1,64 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "DpiCalculator.h" - -#include -#include -#import - - -Q_DECLARE_LOGGING_CATEGORY(qml) - -using namespace governikus; - -#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) -#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) -#define IS_RETINA ([[UIScreen mainScreen] scale] >= 2.0) - -// screen height is orientation dependent -#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width) -#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height) -#define SCREEN_MAX_LENGTH (qMax(SCREEN_WIDTH, SCREEN_HEIGHT)) - -#define IS_IPHONE_4 (IS_IPHONE && SCREEN_MAX_LENGTH == 480.0 && IS_RETINA) -#define IS_IPHONE_5 (IS_IPHONE && SCREEN_MAX_LENGTH == 568.0) -#define IS_IPHONE_6 (IS_IPHONE && SCREEN_MAX_LENGTH == 667.0) -#define IS_IPHONE_6PLUS (IS_IPHONE && SCREEN_MAX_LENGTH == 736.0) - - -qreal DpiCalculator::getDpi() -{ - qreal dpi; - qreal renderFactor = [UIScreen mainScreen].scale; - - if (IS_IPHONE_4 || IS_IPHONE_5 || IS_IPHONE_6) - { - dpi = 326.0; - } - else if (IS_IPHONE_6PLUS) - { - dpi = 401.0; - renderFactor /= 1.15; // the iPhone 6 plus is downsampled - } - else if (IS_IPAD) - { - dpi = 264.0; - } - else - { - qCCritical(qml) << "Unknown iOS device, cannot determine dpi"; - return -1.0; - } - - /* - * For some reason on iOS the unit is interpreted per point, not per pixel. - * Therefore we adapt it to points per inch by dividing by the render factor. - * See: http://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions - */ - dpi /= renderFactor; - - qCInfo(qml) << "Determined dpi" << dpi; - return dpi; -} diff --git a/src/ui/qml/HistoryModel.cpp b/src/ui/qml/HistoryModel.cpp index 5fcd7ff..196d2fe 100644 --- a/src/ui/qml/HistoryModel.cpp +++ b/src/ui/qml/HistoryModel.cpp @@ -7,113 +7,28 @@ #include "HistoryModel.h" #include "asn1/AccessRoleAndRight.h" +#include "AppSettings.h" +#include "Env.h" + +#include "PdfExporter.h" #include "ProviderConfiguration.h" #include "ProviderModel.h" -#include #include -#include using namespace governikus; -namespace +auto& HistoryModel::getHistorySettings() { -bool matchProviderWithSubjectUrl(const ProviderConfigurationInfo& pProvider, const QString& pSubjectUrl) -{ - const QString subjectUrlHost = QUrl(pSubjectUrl).host(); - - // Check provider address host. - if (QUrl(pProvider.getAddress()).host() == subjectUrlHost) - { - return true; - } - - // Check subject urls. - for (const auto& subjectUrl : pProvider.getSubjectUrls()) - { - if (QUrl(subjectUrl).host() == subjectUrlHost) - { - return true; - } - } - - return false; + return Env::getSingleton()->getHistorySettings(); } -} // namespace - - -HistoryProxyModel::HistoryProxyModel() -{ -} - - -HistoryProxyModel::~HistoryProxyModel() -{ -} - - -bool HistoryProxyModel::removeRows(int pRow, int pCount, const QModelIndex& pParent) -{ - return QSortFilterProxyModel::removeRows(pRow, pCount, pParent); -} - - -bool ProviderNameFilterModel::filterAcceptsRow(int pSourceRow, const QModelIndex& /* pSourceParent */) const -{ - HistoryModel* const dataSourceModel = qobject_cast(sourceModel()); - if (dataSourceModel == nullptr) - { - return false; - } - - auto entry = mHistorySettings->getHistoryInfos()[pSourceRow]; - - return matchProviderWithSubjectUrl(mProvider, entry.getSubjectUrl()); -} - - -ProviderNameFilterModel::ProviderNameFilterModel(HistorySettings* pHistorySettings) - : mHistorySettings(pHistorySettings) -{ -} - - -ProviderNameFilterModel::~ProviderNameFilterModel() -{ -} - - -void ProviderNameFilterModel::setProviderAddress(const QString& pProviderAddress) -{ - if (mProvider.getAddress() != pProviderAddress) - { - const auto& providers = Env::getSingleton()->getProviderConfigurationInfos(); - for (const auto& provider : providers) - { - if (provider.getAddress() == pProviderAddress) - { - mProvider = provider; - - invalidateFilter(); - - return; - } - } - - // If we get here, no provider for pProviderAddress was found in the settings. - qWarning() << "Cannot select provider with address" << pProviderAddress; - } -} - - -HistoryModel::HistoryModel(HistorySettings* pHistorySettings, QObject* pParent) +HistoryModel::HistoryModel(QObject* pParent) : QAbstractListModel(pParent) - , mHistorySettings(pHistorySettings) , mFilterModel() - , mNameFilterModel(pHistorySettings) + , mNameFilterModel() , mHistoryModelSearchFilter() { updateConnections(); @@ -121,8 +36,10 @@ HistoryModel::HistoryModel(HistorySettings* pHistorySettings, QObject* pParent) mFilterModel.setFilterCaseSensitivity(Qt::CaseInsensitive); mNameFilterModel.setSourceModel(this); mHistoryModelSearchFilter.setSourceModel(this); - connect(mHistorySettings.data(), &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); - connect(mHistorySettings.data(), &HistorySettings::fireEnabledChanged, this, &HistoryModel::fireEnabledChanged); + + const auto& historySettings = getHistorySettings(); + connect(&historySettings, &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); + connect(&historySettings, &HistorySettings::fireEnabledChanged, this, &HistoryModel::fireEnabledChanged); connect(Env::getSingleton(), &ProviderConfiguration::fireUpdated, this, &HistoryModel::onProvidersChanged); QQmlEngine::setObjectOwnership(&mFilterModel, QQmlEngine::CppOwnership); @@ -144,7 +61,7 @@ void HistoryModel::updateConnections() } mConnections.clear(); - const auto& historyEntries = mHistorySettings->getHistoryInfos(); + const auto& historyEntries = getHistorySettings().getHistoryInfos(); mConnections.reserve(historyEntries.size() * 2); for (int i = 0; i < historyEntries.size(); i++) { @@ -192,7 +109,7 @@ void HistoryModel::onProvidersChanged() int HistoryModel::rowCount(const QModelIndex&) const { - return mHistorySettings->getHistoryInfos().size(); + return getHistorySettings().getHistoryInfos().size(); } @@ -200,7 +117,8 @@ QVariant HistoryModel::data(const QModelIndex& pIndex, int pRole) const { if (pIndex.isValid() && pIndex.row() < rowCount()) { - auto entry = mHistorySettings->getHistoryInfos()[pIndex.row()]; + const auto& infos = getHistorySettings().getHistoryInfos(); + const auto& entry = infos[pIndex.row()]; if (pRole == Qt::DisplayRole || pRole == SUBJECT) { return entry.getSubjectName(); @@ -215,7 +133,8 @@ QVariant HistoryModel::data(const QModelIndex& pIndex, int pRole) const } if (pRole == TERMSOFUSAGE) { - return entry.getTermOfUsage(); + auto tos = entry.getTermOfUsage(); + return tos.remove(QLatin1Char('\r')).replace(QLatin1Char('\t'), QLatin1Char(' ')); } if (pRole == REQUESTEDDATA) { @@ -308,7 +227,7 @@ ProviderConfigurationInfo HistoryModel::determineProviderFor(const HistoryInfo& const auto& providers = Env::getSingleton()->getProviderConfigurationInfos(); for (const auto& provider : providers) { - if (matchProviderWithSubjectUrl(provider, pHistoryInfo.getSubjectUrl())) + if (provider.matchWithSubjectUrl(pHistoryInfo.getSubjectUrl())) { matchingProviders += provider; } @@ -323,7 +242,7 @@ ProviderConfigurationInfo HistoryModel::determineProviderFor(const HistoryInfo& bool HistoryModel::isEnabled() const { - return mHistorySettings->isEnabled(); + return getHistorySettings().isEnabled(); } @@ -331,8 +250,9 @@ void HistoryModel::setEnabled(bool pEnabled) { if (pEnabled != isEnabled()) { - mHistorySettings->setEnabled(pEnabled); - mHistorySettings->save(); + auto& historySettings = getHistorySettings(); + historySettings.setEnabled(pEnabled); + historySettings.save(); } } @@ -366,17 +286,22 @@ QHash HistoryModel::roleNames() const bool HistoryModel::removeRows(int pRow, int pCount, const QModelIndex& pParent) { + if (pCount <= 0) + { + return false; + } beginRemoveRows(pParent, pRow, pRow + pCount - 1); - auto entries = mHistorySettings->getHistoryInfos(); + auto& historySettings = getHistorySettings(); + auto entries = historySettings.getHistoryInfos(); entries.remove(pRow, pCount); // disconnect the signal, otherwise this model gets reset - disconnect(mHistorySettings.data(), &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); - mHistorySettings->setHistoryInfos(entries); - connect(mHistorySettings.data(), &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); + disconnect(&historySettings, &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); + historySettings.setHistoryInfos(entries); + connect(&historySettings, &HistorySettings::fireHistoryInfosChanged, this, &HistoryModel::onHistoryEntriesChanged); - mHistorySettings->save(); + historySettings.save(); endRemoveRows(); return true; @@ -399,3 +324,10 @@ HistoryModelSearchFilter* HistoryModel::getHistoryModelSearchFilter() { return &mHistoryModelSearchFilter; } + + +void HistoryModel::exportHistory(const QUrl& pFilename) const +{ + PdfExporter exporter(pFilename.toLocalFile()); + exporter.exportHistory(); +} diff --git a/src/ui/qml/HistoryModel.h b/src/ui/qml/HistoryModel.h index 830019b..ddf1b39 100644 --- a/src/ui/qml/HistoryModel.h +++ b/src/ui/qml/HistoryModel.h @@ -7,53 +7,19 @@ #pragma once #include "HistoryModelSearchFilter.h" +#include "HistoryProxyModel.h" #include "HistorySettings.h" #include "ProviderConfigurationInfo.h" +#include "ProviderNameFilterModel.h" #include -#include -#include + + +class test_HistoryModel; namespace governikus { -class HistoryProxyModel - : public QSortFilterProxyModel -{ - Q_OBJECT - - public: - Q_INVOKABLE bool removeRows(int pRow, int pCount, const QModelIndex& pParent = QModelIndex()) override; - - HistoryProxyModel(); - - virtual ~HistoryProxyModel() override; -}; - - -class ProviderNameFilterModel - : public QSortFilterProxyModel -{ - Q_OBJECT - - private: - QPointer mHistorySettings; - - ProviderConfigurationInfo mProvider; - - protected: - bool filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const override; - - public: - ProviderNameFilterModel(HistorySettings* pHistorySettings); - - virtual ~ProviderNameFilterModel() override; - - Q_INVOKABLE void setProviderAddress(const QString& pProviderAddress); - -}; - - class HistoryModel : public QAbstractListModel { @@ -63,15 +29,16 @@ class HistoryModel Q_PROPERTY(HistoryModelSearchFilter * searchFilter READ getHistoryModelSearchFilter CONSTANT) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY fireEnabledChanged) - QPointer mHistorySettings; HistoryProxyModel mFilterModel; ProviderNameFilterModel mNameFilterModel; HistoryModelSearchFilter mHistoryModelSearchFilter; private: + friend class ::test_HistoryModel; QVector mConnections; ProviderConfigurationInfo determineProviderFor(const HistoryInfo& pHistoryInfo) const; + static auto& getHistorySettings(); bool isEnabled() const; void setEnabled(bool pEnabled); @@ -85,7 +52,7 @@ class HistoryModel void fireEnabledChanged(bool pValue); public: - HistoryModel(HistorySettings* pHistorySettings, QObject* pParent = nullptr); + explicit HistoryModel(QObject* pParent = nullptr); virtual ~HistoryModel() override; enum HistoryRoles @@ -120,6 +87,8 @@ class HistoryModel Q_INVOKABLE HistoryProxyModel* getFilterModel(); Q_INVOKABLE ProviderNameFilterModel* getNameFilterModel(); HistoryModelSearchFilter* getHistoryModelSearchFilter(); + + Q_INVOKABLE void exportHistory(const QUrl& pFilename) const; }; } // namespace governikus diff --git a/src/ui/qml/HistoryProxyModel.cpp b/src/ui/qml/HistoryProxyModel.cpp new file mode 100644 index 0000000..796066f --- /dev/null +++ b/src/ui/qml/HistoryProxyModel.cpp @@ -0,0 +1,23 @@ +/*! + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "HistoryProxyModel.h" + +using namespace governikus; + + +HistoryProxyModel::HistoryProxyModel() +{ +} + + +HistoryProxyModel::~HistoryProxyModel() +{ +} + + +bool HistoryProxyModel::removeRows(int pRow, int pCount, const QModelIndex& pParent) +{ + return QSortFilterProxyModel::removeRows(pRow, pCount, pParent); +} diff --git a/src/ui/qml/HistoryProxyModel.h b/src/ui/qml/HistoryProxyModel.h new file mode 100644 index 0000000..9142715 --- /dev/null +++ b/src/ui/qml/HistoryProxyModel.h @@ -0,0 +1,26 @@ +/*! + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class HistoryProxyModel + : public QSortFilterProxyModel +{ + Q_OBJECT + + public: + Q_INVOKABLE bool removeRows(int pRow, int pCount, const QModelIndex& pParent = QModelIndex()) override; + + HistoryProxyModel(); + + virtual ~HistoryProxyModel() override; +}; + + +} // namespace governikus diff --git a/src/ui/qml/LogModel.cpp b/src/ui/qml/LogModel.cpp index 1ae770a..b92d436 100644 --- a/src/ui/qml/LogModel.cpp +++ b/src/ui/qml/LogModel.cpp @@ -6,8 +6,11 @@ #include "LanguageLoader.h" #include "LogHandler.h" +#include "PlatformHelper.h" +#include "SettingsModel.h" #include "SingletonHelper.h" +#include #include @@ -17,42 +20,6 @@ using namespace governikus; defineSingleton(LogModel) -double LogModel::getSizeRatio() const -{ - if (mLogEntries.isEmpty()) - { - return 1.0; - } - - return mCount / static_cast(mLogEntries.size()); -} - - -double LogModel::getPosition() const -{ - if (mLogEntries.isEmpty()) - { - return 0.0; - } - - return mIndex / static_cast(mLogEntries.size()); -} - - -void LogModel::setPosition(double pPosition) -{ - beginResetModel(); - - const int newIndex = qFloor(mLogEntries.size() * pPosition); - const int maxIndex = mLogEntries.size() - mCount; - mIndex = newIndex < maxIndex ? newIndex : maxIndex; - - endResetModel(); - - Q_EMIT fireVisibleAreaChanged(); -} - - void LogModel::addLogEntry(const QString& mEntry) { const int cutIndex = mEntry.indexOf(QLatin1String(" : ")); @@ -79,12 +46,7 @@ void LogModel::setLogEntries(QTextStream& pTextStream) addLogEntry(pTextStream.readLine()); } - mIndex = 0; - mCount = mLogEntries.size() < 80 ? mLogEntries.size() : 80; - endResetModel(); - - Q_EMIT fireVisibleAreaChanged(); } @@ -93,28 +55,21 @@ void LogModel::onNewLogMsg(const QString& pMsg) if (mSelectedLogFile == 0) { const int oldSize = mLogEntries.size(); - const bool flickDown = (mIndex + mCount == oldSize); addLogEntry(pMsg); - Q_EMIT fireVisibleAreaChanged(); - - if (flickDown && mAutoFlick) - { - moveView(mLogEntries.size() - oldSize); - Q_EMIT fireNewLogMsg(); - } + beginInsertRows(QModelIndex(), oldSize, mLogEntries.size() - 1); + endInsertRows(); + Q_EMIT fireNewLogMsg(); } } LogModel::LogModel() : QAbstractListModel() - , mIndex(0) - , mCount(0) , mLogFiles() , mSelectedLogFile(0) , mLogEntries() - , mAutoFlick(false) { + connect(Env::getSingleton(), &SettingsModel::fireLanguageChanged, this, &LogModel::fireLogFilesChanged); // needed to translate the "Current log" entry on language change } @@ -131,6 +86,7 @@ QStringList LogModel::getLogfiles() const auto& logHandler = Env::getSingleton(); QStringList logFileNames; + //: LABEL ALL_PLATFORMS logFileNames += tr("Current log"); const auto& logFiles = logHandler->getOtherLogfiles(); for (const auto& entry : logFiles) @@ -144,6 +100,17 @@ QStringList LogModel::getLogfiles() } +QDateTime LogModel::getCurrentLogfileDate() const +{ + if (mSelectedLogFile == 0) + { + return Env::getSingleton()->getCurrentLogfileDate(); + } + + return LogHandler::getFileDate(QFileInfo(mLogFiles.at(mSelectedLogFile))); +} + + void LogModel::removeOtherLogfiles() { if (Env::getSingleton()->removeOtherLogfiles()) @@ -186,72 +153,39 @@ void LogModel::setLogfile(int pIndex) } -void LogModel::moveView(int pDistance) +void LogModel::saveCurrentLogfile(const QUrl& pFilename) const { - if (pDistance == 0) + const QString logfilePath = mLogFiles.at(mSelectedLogFile); + + if (logfilePath.isEmpty()) { - return; + const auto& logHandler = Env::getSingleton(); + logHandler->copy(pFilename.toLocalFile()); } - - int newIndex = mIndex + pDistance; - if (newIndex < 0) + else { - newIndex = 0; - } - - const int maxIndex = mLogEntries.size() - mCount; - if (newIndex > maxIndex) - { - newIndex = maxIndex; - } - - if (newIndex != mIndex) - { - pDistance = newIndex - mIndex; - - if (pDistance < 0) - { - if (mCount + pDistance >= 0) - { - beginRemoveRows(QModelIndex(), mCount + pDistance, mCount - 1); - mCount += pDistance; - endRemoveRows(); - } - - beginInsertRows(QModelIndex(), 0, -pDistance - 1); - mCount -= pDistance; - mIndex += pDistance; - endInsertRows(); - } - else - { - if (mCount - pDistance >= 0) - { - beginRemoveRows(QModelIndex(), 0, pDistance - 1); - mCount -= pDistance; - mIndex += pDistance; - endRemoveRows(); - } - - beginInsertRows(QModelIndex(), mCount, mCount + pDistance - 1); - mCount += pDistance; - endInsertRows(); - } - - Q_EMIT fireVisibleAreaChanged(); + QFile::copy(logfilePath, pFilename.toLocalFile()); } } int LogModel::rowCount(const QModelIndex& pIndex) const { - Q_UNUSED(pIndex); - return mCount; + Q_UNUSED(pIndex) + return mLogEntries.size(); } QVariant LogModel::data(const QModelIndex& pIndex, int pRole) const { - Q_UNUSED(pRole); - return mLogEntries.at(mIndex + pIndex.row()); + Q_UNUSED(pRole) + return mLogEntries.at(pIndex.row()); +} + + +QString LogModel::createLogFileName(const QDateTime& pDateTime) +{ + QString dateFormat = QStringLiteral("yyyy-MM-dd_HH-mm"); + QString logFileDate = pDateTime.toString(dateFormat); + return QStringLiteral("%1-%2.log").arg(QCoreApplication::applicationName(), logFileDate); } diff --git a/src/ui/qml/LogModel.h b/src/ui/qml/LogModel.h index 659edf5..eb0f657 100644 --- a/src/ui/qml/LogModel.h +++ b/src/ui/qml/LogModel.h @@ -4,11 +4,16 @@ #pragma once +#include "Env.h" + #include +#include #include +#include #include #include #include +#include class test_LogModel; @@ -19,24 +24,16 @@ class LogModel : public QAbstractListModel { Q_OBJECT + friend class Env; - Q_PROPERTY(bool autoFlick MEMBER mAutoFlick) Q_PROPERTY(QStringList logFiles READ getLogfiles NOTIFY fireLogFilesChanged) - Q_PROPERTY(double heightRatio READ getSizeRatio NOTIFY fireVisibleAreaChanged) - Q_PROPERTY(double yPosition READ getPosition WRITE setPosition NOTIFY fireVisibleAreaChanged) private: friend class ::test_LogModel; - int mIndex; - int mCount; QStringList mLogFiles; int mSelectedLogFile; QStringList mLogEntries; - bool mAutoFlick; - double getSizeRatio() const; - double getPosition() const; - void setPosition(double pPosition); void addLogEntry(const QString& mEntry); void setLogEntries(QTextStream& pTextStream); @@ -45,26 +42,28 @@ class LogModel protected: LogModel(); - - public: static LogModel& getInstance(); + public: QStringList getLogfiles(); + Q_INVOKABLE QDateTime getCurrentLogfileDate() const; Q_INVOKABLE void removeOtherLogfiles(); Q_INVOKABLE void removeCurrentLogfile(); Q_INVOKABLE void setLogfile(int pIndex); - Q_INVOKABLE void moveView(int pDistance); + Q_INVOKABLE void saveCurrentLogfile(const QUrl& pFilename) const; Q_INVOKABLE void mailLog(const QString& pEmail = tr("support.ausweisapp2@governikus.de"), - const QString& pSubject = tr("Android log file"), + const QString& pSubject = tr("Mobile log file"), const QString& pMsg = tr("")); - Q_INVOKABLE void shareLog(); + + /// \a popupPosition will be used on an iPad as the origin of the share bubble + Q_INVOKABLE void shareLog(QPoint popupPosition); int rowCount(const QModelIndex& pIndex = QModelIndex()) const override; QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + static QString createLogFileName(const QDateTime& pDateTime = QDateTime::currentDateTime()); Q_SIGNALS: - void fireVisibleAreaChanged(); void fireLogFilesChanged(); void fireNewLogMsg(); }; diff --git a/src/ui/qml/LogModel_android.cpp b/src/ui/qml/LogModel_android.cpp index 56f31e9..c6408d6 100644 --- a/src/ui/qml/LogModel_android.cpp +++ b/src/ui/qml/LogModel_android.cpp @@ -63,8 +63,7 @@ static QString getPublicLogFileName(QAndroidJniEnvironment& env, const QAndroidJ return QString(); } - const auto& nowAsISO = pDateTime.toString(QStringLiteral("yyyy-MM-dd'T'HH:mm:ss-t")); - return QStringLiteral("%1/AusweisApp2-%2.txt").arg(jFilesDirPath.toString(), nowAsISO); + return QStringLiteral("%1/%2").arg(jFilesDirPath.toString(), LogModel::createLogFileName(pDateTime)); } @@ -83,6 +82,7 @@ void LogModel::mailLog(const QString& pEmail, const QString& pSubject, const QSt const auto& jEmail = QAndroidJniObject::fromString(pEmail); const auto& jSubject = QAndroidJniObject::fromString(pSubject); const auto& jMsg = QAndroidJniObject::fromString(pMsg); + //: LABEL ANDROID const auto& jChooserTitle = QAndroidJniObject::fromString(tr("Send application log per email...")); const auto& publicLogFile = getPublicLogFileName(env, javaActivity, external); const auto& jPublicLogFile = QAndroidJniObject::fromString(publicLogFile); @@ -112,7 +112,7 @@ void LogModel::mailLog(const QString& pEmail, const QString& pSubject, const QSt } -void LogModel::shareLog() +void LogModel::shareLog(const QPoint /*popupPosition*/) { QAndroidJniEnvironment env; const QAndroidJniObject javaActivity(QtAndroid::androidActivity()); @@ -150,6 +150,7 @@ void LogModel::shareLog() } } + //: LABEL ANDROID const auto& jChooserTitle = QAndroidJniObject::fromString(tr("Share application log...")); const auto& jPublicLogFile = QAndroidJniObject::fromString(publicLogFile); diff --git a/src/ui/qml/LogModel_generic.cpp b/src/ui/qml/LogModel_generic.cpp index e45cd85..b5b6a1d 100644 --- a/src/ui/qml/LogModel_generic.cpp +++ b/src/ui/qml/LogModel_generic.cpp @@ -15,15 +15,15 @@ using namespace governikus; void LogModel::mailLog(const QString& pEmail, const QString& pSubject, const QString& pMsg) { - Q_UNUSED(pEmail); - Q_UNUSED(pSubject); - Q_UNUSED(pMsg); + Q_UNUSED(pEmail) + Q_UNUSED(pSubject) + Q_UNUSED(pMsg) qCWarning(qml) << "NOT IMPLEMENTED"; } -void LogModel::shareLog() +void LogModel::shareLog(const QPoint /*popupPosition*/) { qCWarning(qml) << "NOT IMPLEMENTED"; } diff --git a/src/ui/qml/LogModel_ios.mm b/src/ui/qml/LogModel_ios.mm new file mode 100644 index 0000000..b44b5b6 --- /dev/null +++ b/src/ui/qml/LogModel_ios.mm @@ -0,0 +1,141 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "LogModel.h" + +#include "ApplicationModel.h" +#include "LogHandler.h" + +#import +#import + +#include +#include + +#include + + +Q_DECLARE_LOGGING_CATEGORY(qml) + + +using namespace governikus; + +@interface MailComposeController + : MFMailComposeViewController +- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error; +@end + +@implementation MailComposeController + +- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error { + + Q_UNUSED(result) + Q_UNUSED(error) + + [controller dismissViewControllerAnimated: true completion:nil]; +} + + +@end + + +static std::optional getTemporaryLogFile(std::optional pSourceFile = std::nullopt) +{ + LogHandler* logHandler = Env::getSingleton(); + + QString destinationFileName; + if (pSourceFile) + { + destinationFileName = LogModel::createLogFileName(logHandler->getFileDate(QFileInfo(pSourceFile.value()))); + } + else + { + destinationFileName = LogModel::createLogFileName(); + } + + QString destinationFilePath = QString::fromNSString([NSTemporaryDirectory() stringByAppendingPathComponent: destinationFileName.toNSString()]); + + if (QFile::exists(destinationFilePath)) + { + QFile::remove(destinationFilePath); + } + + if (pSourceFile) + { + if (!QFile::copy(pSourceFile.value(), destinationFilePath)) + { + qCCritical(qml) << "Cannot copy log file to" << destinationFilePath; + return std::nullopt; + } + } + else + { + logHandler->copy(destinationFilePath); + } + + return destinationFilePath; +} + + +void LogModel::mailLog(const QString& pEmail, const QString& pSubject, const QString& pMsg) +{ + if (![MFMailComposeViewController canSendMail]) + { + qCWarning(qml) << "Email not configured, cannot send mail."; + Env::getSingleton()->showFeedback(tr("Could not send log! Please configure your mail client first.")); + return; + } + + QString fileName = LogModel::createLogFileName(); + std::optional logFile = getTemporaryLogFile(); + if (!logFile) + { + return; + } + + NSData* fileContent = [NSData dataWithContentsOfFile: logFile.value().toNSString()]; + + UIViewController* rootController = [[UIApplication sharedApplication].windows[0] rootViewController]; + + auto* mailComposeController = [[MailComposeController alloc] init]; + mailComposeController.mailComposeDelegate = mailComposeController; + + [mailComposeController setToRecipients:@[pEmail.toNSString()]]; + [mailComposeController setSubject: pSubject.toNSString()]; + [mailComposeController setMessageBody: pMsg.toNSString() isHTML:NO]; + [mailComposeController addAttachmentData: fileContent mimeType: @"text/plain" fileName: fileName.toNSString()]; + + [rootController presentViewController:mailComposeController animated:YES completion:nil]; +} + + +void LogModel::shareLog(const QPoint popupPosition) +{ + std::optional logFile; + if (mSelectedLogFile == 0) + { + logFile = getTemporaryLogFile(); + } + else + { + logFile = getTemporaryLogFile(mLogFiles.at(mSelectedLogFile)); + } + if (!logFile) + { + return; + } + + NSURL* logFileURL = [NSURL fileURLWithPath: logFile.value().toNSString()]; + + NSArray* shareItems = @[logFileURL]; + + UIActivityViewController* shareController = [[UIActivityViewController alloc]initWithActivityItems: shareItems applicationActivities:nil]; + + UIViewController* rootController = [[UIApplication sharedApplication].windows[0] rootViewController]; + + shareController.popoverPresentationController.sourceView = rootController.view; + shareController.popoverPresentationController.sourceRect = CGRectMake(popupPosition.x(), popupPosition.y(), 0, 0); + + [rootController presentViewController:shareController animated:YES completion: nil]; +} diff --git a/src/ui/qml/NotificationModel.cpp b/src/ui/qml/NotificationModel.cpp new file mode 100644 index 0000000..b73e817 --- /dev/null +++ b/src/ui/qml/NotificationModel.cpp @@ -0,0 +1,100 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "NotificationModel.h" + +#include "LogHandler.h" +#include "SingletonHelper.h" + +#include + + +using namespace governikus; + + +defineSingleton(NotificationModel) + + +QString NotificationModel::getLastType() const +{ + if (mNotificationEntries.isEmpty()) + { + return QString(); + } + + return qAsConst(mNotificationEntries).last().mType; +} + + +void NotificationModel::onNewLogMsg(const QString& pMsg, const QString& pCategoryName) +{ + if (pCategoryName == QLatin1String("developermode") || pCategoryName == QLatin1String("feedback")) + { + if (mNotificationEntries.isFull()) + { + beginRemoveRows(QModelIndex(), 0, 0); + mNotificationEntries.removeFirst(); + endRemoveRows(); + } + + beginInsertRows(QModelIndex(), mNotificationEntries.size(), mNotificationEntries.size()); + const auto& time = QTime::currentTime().toString(tr("hh:mm:ss")); + mNotificationEntries.append({pCategoryName, time, pMsg}); + endInsertRows(); + + Q_EMIT fireLastTypeChanged(); + } +} + + +NotificationModel::NotificationModel() + : QAbstractListModel() + , mNotificationEntries(20) +{ + connect(Env::getSingleton(), &LogHandler::fireRawLog, this, &NotificationModel::onNewLogMsg, Qt::QueuedConnection); +} + + +NotificationModel& NotificationModel::getInstance() +{ + return *Instance; +} + + +int NotificationModel::rowCount(const QModelIndex& pIndex) const +{ + Q_UNUSED(pIndex) + return mNotificationEntries.count(); +} + + +QVariant NotificationModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (mNotificationEntries.isEmpty() || pIndex.row() >= mNotificationEntries.size()) + { + return QVariant(); + } + const auto& notification = qAsConst(mNotificationEntries).at(mNotificationEntries.firstIndex() + pIndex.row()); + switch (pRole) + { + case TYPE: + return notification.mType; + + case TIME: + return notification.mTime; + + default: + return notification.mText; + } +} + + +QHash NotificationModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(TYPE, "type"); + roles.insert(TIME, "time"); + roles.insert(TEXT, "text"); + return roles; +} diff --git a/src/ui/qml/NotificationModel.h b/src/ui/qml/NotificationModel.h new file mode 100644 index 0000000..0ee43c2 --- /dev/null +++ b/src/ui/qml/NotificationModel.h @@ -0,0 +1,65 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "Env.h" + +#include +#include +#include +#include +#include +#include + +class test_NotificationModel; + +namespace governikus +{ + +class NotificationModel + : public QAbstractListModel +{ + Q_OBJECT + friend class Env; + friend class ::test_NotificationModel; + + Q_PROPERTY(QString lastType READ getLastType NOTIFY fireLastTypeChanged) + + enum UserRoles + { + TYPE = Qt::UserRole + 1, + TIME, + TEXT + }; + + struct NotificationEntry + { + QString mType; + QString mTime; + QString mText; + }; + + private: + QContiguousCache mNotificationEntries; + + QString getLastType() const; + + private Q_SLOTS: + void onNewLogMsg(const QString& pMsg, const QString& pCategoryName); + + protected: + NotificationModel(); + static NotificationModel& getInstance(); + + public: + int rowCount(const QModelIndex& pIndex) const override; + QVariant data(const QModelIndex& pIndex, int pRole) const override; + QHash roleNames() const override; + + Q_SIGNALS: + void fireLastTypeChanged(); +}; + +} // namespace governikus diff --git a/src/ui/qml/NumberModel.cpp b/src/ui/qml/NumberModel.cpp index a3849ec..ad8ba63 100644 --- a/src/ui/qml/NumberModel.cpp +++ b/src/ui/qml/NumberModel.cpp @@ -21,6 +21,7 @@ NumberModel::NumberModel() : QObject() , mContext() , mRequestTransportPin(false) + , mRequestNewPin(false) { const auto readerManager = Env::getSingleton(); connect(readerManager, &ReaderManager::fireReaderPropertiesUpdated, this, &NumberModel::onReaderInfoChanged); @@ -45,6 +46,7 @@ void NumberModel::resetContext(const QSharedPointer& pContext) connect(mContext.data(), &WorkflowContext::fireCanChanged, this, &NumberModel::fireCanChanged); connect(mContext.data(), &WorkflowContext::firePinChanged, this, &NumberModel::firePinChanged); connect(mContext.data(), &WorkflowContext::fireCanAllowedModeChanged, this, &NumberModel::fireCanAllowedModeChanged); + connect(mContext.data(), &WorkflowContext::firePasswordTypeChanged, this, &NumberModel::firePasswordTypeChanged); const auto changePinContext = mContext.objectCast(); if (changePinContext) @@ -55,7 +57,8 @@ void NumberModel::resetContext(const QSharedPointer& pContext) connect(mContext.data(), &WorkflowContext::fireCardConnectionChanged, this, &NumberModel::onCardConnectionChanged); connect(mContext.data(), &WorkflowContext::fireReaderNameChanged, this, &NumberModel::fireReaderInfoChanged); - connect(mContext.data(), &WorkflowContext::fireLastPaceResultChanged, this, &NumberModel::fireInputErrorChanged); + connect(mContext.data(), &WorkflowContext::fireReaderNameChanged, this, &NumberModel::fireInputErrorChanged); + connect(mContext.data(), &WorkflowContext::firePaceResultUpdated, this, &NumberModel::fireInputErrorChanged); // The length of the pin doesn't matter for the core. Requesting // a 5 or 6 digits PIN is only part of the gui. Therefore we handle @@ -71,6 +74,7 @@ void NumberModel::resetContext(const QSharedPointer& pContext) { mRequestTransportPin = false; } + mRequestNewPin = false; Q_EMIT fireCanChanged(); Q_EMIT firePinChanged(); @@ -80,12 +84,48 @@ void NumberModel::resetContext(const QSharedPointer& pContext) Q_EMIT fireReaderInfoChanged(); Q_EMIT fireCanAllowedModeChanged(); Q_EMIT fireRequestTransportPinChanged(); + Q_EMIT firePasswordTypeChanged(); } -PacePasswordId NumberModel::getEstablishPaceChannelType() const +NumberModel::QmlPasswordType NumberModel::getPasswordType() const { - return mContext ? mContext->getEstablishPaceChannelType() : PacePasswordId::UNKNOWN; + if (!mContext) + { + return QmlPasswordType::PASSWORD_PIN; + } + + if (mRequestNewPin) + { + return QmlPasswordType::PASSWORD_NEW_PIN; + } + + switch (mContext->getEstablishPaceChannelType()) + { + case PacePasswordId::UNKNOWN: + case PacePasswordId::PACE_MRZ: + case PacePasswordId::PACE_PIN: + return QmlPasswordType::PASSWORD_PIN; + + case PacePasswordId::PACE_CAN: + return QmlPasswordType::PASSWORD_CAN; + + case PacePasswordId::PACE_PUK: + return QmlPasswordType::PASSWORD_PUK; + } + + Q_UNREACHABLE(); + return QmlPasswordType::PASSWORD_PIN; +} + + +void NumberModel::requestNewPin() +{ + if (!mRequestNewPin) + { + mRequestNewPin = true; + Q_EMIT firePasswordTypeChanged(); + } } @@ -140,6 +180,9 @@ void NumberModel::setNewPin(const QString& pNewPin) { remoteServiceContext->setNewPin(pNewPin); } + + mRequestNewPin = false; + Q_EMIT firePasswordTypeChanged(); } @@ -158,12 +201,18 @@ void NumberModel::setPuk(const QString& pPuk) } -bool NumberModel::hasError() +bool NumberModel::hasError() const { return getInputErrorCode() != CardReturnCode::OK || Env::getSingleton()->isExtendedLengthApdusUnsupported() || isPinDeactivated(); } +bool NumberModel::hasPasswordError() const +{ + return CardReturnCodeUtil::equalsWrongPacePassword(getInputErrorCode()); +} + + CardReturnCode NumberModel::getInputErrorCode() const { if (mContext.isNull() @@ -187,22 +236,27 @@ QString NumberModel::getInputError() const return QString(); case CardReturnCode::INVALID_PIN: + //: INFO ALL_PLATFORMS The wrong PIN was entered on the first attempt. return tr("The given PIN is not correct. You have 2 tries to enter the correct PIN."); case CardReturnCode::INVALID_PIN_2: + //: INFO ALL_PLATFORMS The wrong PIN was entered twice, the next attempt requires the CAN for additional verification. return tr("You have entered the wrong PIN twice. " "Prior to a third attempt, you have to enter your six-digit card access number first. " "You can find your card access number on the front of your ID card."); case CardReturnCode::INVALID_PIN_3: + //: INFO ALL_PLATFORMS The PIN was entered wrongfully three times, the id card needs to be unlocked using the PUK. return tr("You have entered a wrong PIN three times. " "Your PIN is now blocked. " "You have to enter the PUK now for unblocking."); case CardReturnCode::INVALID_CAN: + //: INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. return tr("You have entered a wrong CAN, please try again."); case CardReturnCode::INVALID_PUK: + //: INFO ALL_PLATFORMS The PUK entered wrongfully and needs to be supplied again. return tr("You have entered a wrong PUK. " "Please try again."); diff --git a/src/ui/qml/NumberModel.h b/src/ui/qml/NumberModel.h index 9d7f46d..4288917 100644 --- a/src/ui/qml/NumberModel.h +++ b/src/ui/qml/NumberModel.h @@ -13,8 +13,6 @@ #include #include -class test_NumberModel; - namespace governikus { @@ -23,14 +21,14 @@ class NumberModel { Q_OBJECT friend class Env; - friend class ::test_NumberModel; - Q_PROPERTY(governikus::PacePasswordId establishPaceChannelType READ getEstablishPaceChannelType) + Q_PROPERTY(QmlPasswordType passwordType READ getPasswordType NOTIFY firePasswordTypeChanged) Q_PROPERTY(QString can READ getCan WRITE setCan NOTIFY fireCanChanged) Q_PROPERTY(QString pin READ getPin WRITE setPin NOTIFY firePinChanged) Q_PROPERTY(QString newPin READ getNewPin WRITE setNewPin NOTIFY fireNewPinChanged) Q_PROPERTY(QString puk READ getPuk WRITE setPuk NOTIFY firePukChanged) - Q_PROPERTY(bool hasError READ hasError NOTIFY fireReaderInfoChanged) + Q_PROPERTY(bool hasError READ hasError NOTIFY fireInputErrorChanged) + Q_PROPERTY(bool hasPasswordError READ hasPasswordError NOTIFY fireInputErrorChanged) Q_PROPERTY(CardReturnCode inputErrorCode READ getInputErrorCode NOTIFY fireInputErrorChanged) Q_PROPERTY(QString inputError READ getInputError NOTIFY fireInputErrorChanged) Q_PROPERTY(int retryCounter READ getRetryCounter NOTIFY fireReaderInfoChanged) @@ -41,6 +39,7 @@ class NumberModel private: QSharedPointer mContext; bool mRequestTransportPin; + bool mRequestNewPin; private Q_SLOTS: void onCardConnectionChanged(); @@ -51,9 +50,16 @@ class NumberModel static NumberModel& getInstance(); public: + enum class QmlPasswordType + { + PASSWORD_PIN, PASSWORD_CAN, PASSWORD_PUK, PASSWORD_NEW_PIN, PASSWORD_REMOTE_PIN + }; + Q_ENUM(QmlPasswordType) + void resetContext(const QSharedPointer& pContext = QSharedPointer()); - PacePasswordId getEstablishPaceChannelType() const; + QmlPasswordType getPasswordType() const; + Q_INVOKABLE void requestNewPin(); QString getCan() const; void setCan(const QString& pCan); @@ -67,7 +73,8 @@ class NumberModel QString getPuk() const; void setPuk(const QString& pPuk); - bool hasError(); + bool hasError() const; + bool hasPasswordError() const; CardReturnCode getInputErrorCode() const; QString getInputError() const; @@ -90,6 +97,7 @@ class NumberModel void fireReaderInfoChanged(); void fireCanAllowedModeChanged(); void fireRequestTransportPinChanged(); + void firePasswordTypeChanged(); }; diff --git a/src/ui/qml/PlatformHelper.cpp b/src/ui/qml/PlatformHelper.cpp new file mode 100644 index 0000000..d42b7d3 --- /dev/null +++ b/src/ui/qml/PlatformHelper.cpp @@ -0,0 +1,61 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "PlatformHelper.h" + +#include +#include +#include +#include + +namespace governikus +{ + +bool isPlatform(const QObject& object, const Platform selector) +{ + if (const QQmlContext* const context = QQmlEngine::contextForObject(&object)) + { + // The UI could have been changed using OVERRIDE_PLATFORM_SELECTOR + if (QQmlEngine* const engine = context->engine()) + { + const QStringList selectors = QQmlFileSelector::get(engine)->selector()->extraSelectors(); + switch (selector) + { + case Platform::ANDROID: + return selectors.contains(QLatin1String("android")); + + case Platform::IOS: + return selectors.contains(QLatin1String("ios")); + + case Platform::MOBILE: + return selectors.contains(QLatin1String("mobile")); + + case Platform::DESKTOP: + return selectors.contains(QLatin1String("desktop")); + } + } + } +#ifdef Q_OS_IOS + if (selector == Platform::MOBILE || selector == Platform::IOS) + { + return true; + } +#endif +#ifdef Q_OS_ANDROID + if (selector == Platform::MOBILE || selector == Platform::ANDROID) + { + return true; + } +#endif +#if ((!defined(Q_OS_ANDROID) && defined(Q_OS_LINUX)) || defined(Q_OS_WIN) || defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD)) + if (selector == Platform::DESKTOP) + { + return true; + } +#endif + return false; +} + + +} // namespace governikus diff --git a/src/ui/qml/PlatformHelper.h b/src/ui/qml/PlatformHelper.h new file mode 100644 index 0000000..8a31149 --- /dev/null +++ b/src/ui/qml/PlatformHelper.h @@ -0,0 +1,23 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "EnumHelper.h" + +#include + +namespace governikus +{ + +defineEnumType(Platform, + ANDROID, + IOS, + MOBILE, + DESKTOP) + +/// Determine platform at runtime. Use this instead of the preprocessor to allow running the mobile UI on the desktop. +bool isPlatform(const QObject&, Platform selector); + +} // namespace governikus diff --git a/src/ui/qml/ProviderCategoryFilterModel.cpp b/src/ui/qml/ProviderCategoryFilterModel.cpp index 7493ffd..764228f 100644 --- a/src/ui/qml/ProviderCategoryFilterModel.cpp +++ b/src/ui/qml/ProviderCategoryFilterModel.cpp @@ -7,18 +7,6 @@ using namespace governikus; -namespace -{ -const QStringList& getCategories() -{ - static QStringList cats({QStringLiteral("citizen"), QStringLiteral("insurance"), QStringLiteral("finance"), QStringLiteral("other")}); - return cats; -} - - -} // namespace - - QString ProviderCategoryFilterModel::getSearchString() const { return mSearchString; @@ -45,35 +33,26 @@ QStringList ProviderCategoryFilterModel::getSelectedCategories() const int ProviderCategoryFilterModel::getAdditionalResultCount() const { - int results = 0; - for (const QString& p : getCategories()) - { - results += matchesForExcludedCategory(p); - } - return results; -} - - -int ProviderCategoryFilterModel::matchesForExcludedCategory(const QString& pCategory) const -{ - if (mSearchString.isEmpty() || mSelectedCategories.isEmpty() || mSelectedCategories.contains(pCategory)) + if (mSearchString.isEmpty()) { return 0; } - QAbstractItemModel* const model = sourceModel(); - const int count = model->rowCount(); - int matchCount = 0; - for (int sourceRow = 0; sourceRow < count; ++sourceRow) - { - const QModelIndex idx = model->index(sourceRow, 0, QModelIndex()); - const QString dt = model->data(idx, Qt::DisplayRole).toString(); - if (!dt.contains(mSearchString, Qt::CaseInsensitive)) - { - continue; - } + auto excludedCategories = ProviderModel::getProviderCategories() - mSelectedCategories; + return resultCountForFilter(excludedCategories, mSearchString); +} - if (pCategory.toLower() == model->data(idx, ProviderModel::CATEGORY).toString().toLower()) + +int ProviderCategoryFilterModel::resultCountForFilter(const QSet& pCategories, const QString& pSearchString) const +{ + QAbstractItemModel* const model = sourceModel(); + Q_ASSERT(model != nullptr); + const int rowCount = model->rowCount(); + + int matchCount = 0; + for (int sourceRow = 0; sourceRow < rowCount; ++sourceRow) + { + if (rowMatchesFilter(sourceRow, QModelIndex(), pCategories, pSearchString)) { matchCount++; } @@ -84,22 +63,45 @@ int ProviderCategoryFilterModel::matchesForExcludedCategory(const QString& pCate bool ProviderCategoryFilterModel::filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const +{ + return rowMatchesFilter(pSourceRow, pSourceParent, mSelectedCategories, mSearchString); +} + + +bool ProviderCategoryFilterModel::rowMatchesFilter(int pSourceRow, const QModelIndex& pSourceParent, const QSet& pSelectedCategories, const QString& pSearchString) const { QAbstractItemModel* const model = sourceModel(); Q_ASSERT(model != nullptr); const QModelIndex idx = model->index(pSourceRow, 0, pSourceParent); - if (!mSearchString.isEmpty()) + bool isRowInSelectedCategory = pSelectedCategories.isEmpty() || + pSelectedCategories.contains(QLatin1String("all")) || + pSelectedCategories.contains(model->data(idx, ProviderModel::CATEGORY).toString().toLower()); + + if (!isRowInSelectedCategory) { - const QString dt = model->data(idx, Qt::DisplayRole).toString(); - if (!dt.contains(mSearchString, Qt::CaseInsensitive)) - { - return false; - } + return false; + } + else if (pSearchString.isEmpty()) + { + return true; } - return mSelectedCategories.isEmpty() || mSelectedCategories.contains(QStringLiteral("all")) || - mSelectedCategories.contains(model->data(idx, ProviderModel::CATEGORY).toString().toLower()); + const QString display = model->data(idx, Qt::DisplayRole).toString(); + const QString shortname = model->data(idx, ProviderModel::SHORTNAME).toString(); + const QString longname = model->data(idx, ProviderModel::LONGNAME).toString(); + const QString address = model->data(idx, ProviderModel::ADDRESS).toString(); + const QString homepage = model->data(idx, ProviderModel::HOMEPAGE).toString(); + const QString shortdescription = model->data(idx, ProviderModel::SHORTDESCRIPTION).toString(); + const QString longdescription = model->data(idx, ProviderModel::LONGDESCRIPTION).toString(); + + return display.contains(pSearchString, Qt::CaseInsensitive) || + shortname.contains(pSearchString, Qt::CaseInsensitive) || + longname.contains(pSearchString, Qt::CaseInsensitive) || + address.contains(pSearchString, Qt::CaseInsensitive) || + homepage.contains(pSearchString, Qt::CaseInsensitive) || + shortdescription.contains(pSearchString, Qt::CaseInsensitive) || + longdescription.contains(pSearchString, Qt::CaseInsensitive); } @@ -160,16 +162,18 @@ void ProviderCategoryFilterModel::updateCategorySelection(const QString& pCatego void ProviderCategoryFilterModel::addAdditionalResultCategories() { - bool needUpdate = false; - for (const QString& p : getCategories()) + bool filterChanged = false; + const auto excludedCategories = ProviderModel::getProviderCategories() - mSelectedCategories; + for (const QString& category : excludedCategories) { - if (matchesForExcludedCategory(p) > 0) + if (resultCountForFilter(QSet({category}), mSearchString) > 0) { - needUpdate = true; - mSelectedCategories += p; + filterChanged = true; + mSelectedCategories += category; } } - if (needUpdate) + + if (filterChanged) { invalidateFilter(); Q_EMIT fireCriteriaChanged(); diff --git a/src/ui/qml/ProviderCategoryFilterModel.h b/src/ui/qml/ProviderCategoryFilterModel.h index 610f7d0..8001351 100644 --- a/src/ui/qml/ProviderCategoryFilterModel.h +++ b/src/ui/qml/ProviderCategoryFilterModel.h @@ -38,7 +38,8 @@ class ProviderCategoryFilterModel void updateSearchString(const QString& pSearchString); QStringList getSelectedCategories() const; int getAdditionalResultCount() const; - int matchesForExcludedCategory(const QString& pCategory) const; + int resultCountForFilter(const QSet& pCategories, const QString& pSearchString) const; + bool rowMatchesFilter(int pSourceRow, const QModelIndex& pSourceParent, const QSet& pSelectedCategories, const QString& pSearchString) const; protected: bool filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const override; diff --git a/src/ui/qml/ProviderModel.cpp b/src/ui/qml/ProviderModel.cpp index 114aaad..dd1424f 100644 --- a/src/ui/qml/ProviderModel.cpp +++ b/src/ui/qml/ProviderModel.cpp @@ -14,10 +14,12 @@ QString ProviderModel::createCostString(double pCostsPerMinute, double pCostsPer { if (pCostsPerMinute > 0.0) { + //: INFO ALL_PLATFORMS Unit for expenses for calling the hotline (per minute). return tr("%1/min").arg(createAmountString(pCostsPerMinute)); } if (pCostsPerCall > 0.0) { + //: INFO ALL_PLATFORMS Unit for expenses for calling the hotline (per call). return tr("%1/call").arg(createAmountString(pCostsPerCall)); } return QString(); @@ -26,6 +28,7 @@ QString ProviderModel::createCostString(double pCostsPerMinute, double pCostsPer QString ProviderModel::createAmountString(double pCents) { + //: INFO ALL_PLATFORMS Currency unit for expenses for calling the hotline (Euro/Cent). return pCents > 100 ? tr("%1 EUR").arg(pCents / 100.0) : tr("%1 ct").arg(pCents); } @@ -192,6 +195,13 @@ QHash ProviderModel::roleNames() const } +const QSet& ProviderModel::getProviderCategories() +{ + static QSet cats({QStringLiteral("citizen"), QStringLiteral("insurance"), QStringLiteral("finance"), QStringLiteral("other")}); + return cats; +} + + QString ProviderModel::createCostString(const CallCost& pCosts) { if (pCosts.isNull()) @@ -202,10 +212,13 @@ QString ProviderModel::createCostString(const CallCost& pCosts) QString msg; if (pCosts.getFreeSeconds() > 0) { + //: INFO ALL_PLATFORMS Free of charge seconds when calling the hotline. msg += tr("%1 seconds free, afterwards ").arg(pCosts.getFreeSeconds()); } + //: INFO ALL_PLATFORMS Land line charges when calling the hotline. msg += tr("landline costs %1; ").arg(createCostString(pCosts.getLandlineCentsPerMinute(), pCosts.getLandlineCentsPerCall())); const auto mobileCosts = createCostString(pCosts.getMobileCentsPerMinute(), pCosts.getMobileCentsPerCall()); + //: INFO ALL_PLATFORMS Cell phone charges when calling the hotline. msg += mobileCosts.isEmpty() ? tr("mobile costs may vary.") : tr("mobile costs %1").arg(mobileCosts); return msg; } diff --git a/src/ui/qml/ProviderModel.h b/src/ui/qml/ProviderModel.h index aae2c18..4a64c40 100644 --- a/src/ui/qml/ProviderModel.h +++ b/src/ui/qml/ProviderModel.h @@ -9,19 +9,16 @@ #include "CallCost.h" #include +#include #include -class test_ProviderModel; - - namespace governikus { class ProviderModel : public QAbstractListModel { - friend class ::test_ProviderModel; Q_OBJECT @@ -64,6 +61,8 @@ class ProviderModel QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; QHash roleNames() const override; + static const QSet& getProviderCategories(); + static QString createCostString(const CallCost& pCosts); }; diff --git a/src/ui/qml/ProviderNameFilterModel.cpp b/src/ui/qml/ProviderNameFilterModel.cpp new file mode 100644 index 0000000..8fe41c9 --- /dev/null +++ b/src/ui/qml/ProviderNameFilterModel.cpp @@ -0,0 +1,60 @@ +/*! + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "ProviderNameFilterModel.h" + +#include "AppSettings.h" +#include "Env.h" +#include "HistoryModel.h" +#include "HistorySettings.h" +#include "ProviderConfiguration.h" +#include "ProviderModel.h" + +#include + +using namespace governikus; + + +bool ProviderNameFilterModel::filterAcceptsRow(int pSourceRow, const QModelIndex& /* pSourceParent */) const +{ + auto* const dataSourceModel = qobject_cast(sourceModel()); + if (dataSourceModel == nullptr) + { + return false; + } + + const auto& infos = Env::getSingleton()->getHistorySettings().getHistoryInfos(); + const auto& entry = infos[pSourceRow]; + + return mProvider.matchWithSubjectUrl(entry.getSubjectUrl()); +} + + +ProviderNameFilterModel::ProviderNameFilterModel() + : mProvider() +{ +} + + +void ProviderNameFilterModel::setProviderAddress(const QString& pProviderAddress) +{ + if (mProvider.getAddress() != pProviderAddress) + { + const auto& providers = Env::getSingleton()->getProviderConfigurationInfos(); + for (const auto& provider : providers) + { + if (provider.getAddress() == pProviderAddress) + { + mProvider = provider; + + invalidateFilter(); + + return; + } + } + + // If we get here, no provider for pProviderAddress was found in the settings. + qWarning() << "Cannot select provider with address" << pProviderAddress; + } +} diff --git a/src/ui/qml/ProviderNameFilterModel.h b/src/ui/qml/ProviderNameFilterModel.h new file mode 100644 index 0000000..1af6db9 --- /dev/null +++ b/src/ui/qml/ProviderNameFilterModel.h @@ -0,0 +1,37 @@ +/*! + * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "ProviderConfigurationInfo.h" + +#include +#include + +class test_ProviderNameFilterModel; + +namespace governikus +{ + + +class ProviderNameFilterModel + : public QSortFilterProxyModel +{ + Q_OBJECT + + private: + friend class ::test_ProviderNameFilterModel; + ProviderConfigurationInfo mProvider; + + protected: + bool filterAcceptsRow(int pSourceRow, const QModelIndex& pSourceParent) const override; + + public: + ProviderNameFilterModel(); + + + Q_INVOKABLE void setProviderAddress(const QString& pProviderAddress); +}; + +} // namespace governikus diff --git a/src/ui/qml/QmlExtension.h b/src/ui/qml/QmlExtension.h deleted file mode 100644 index 550162c..0000000 --- a/src/ui/qml/QmlExtension.h +++ /dev/null @@ -1,28 +0,0 @@ -/*! - * \brief Utility for sharing text. - * - * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include - -namespace governikus -{ - -class QmlExtension - : public QObject -{ - Q_OBJECT - - public: - Q_INVOKABLE void showSettings(const QString& pAction); - Q_INVOKABLE void showFeedback(const QString& pMessage); - Q_INVOKABLE bool exportHistory(const QString& pPdfUrl) const; - Q_INVOKABLE void keepScreenOn(bool pActive); - Q_INVOKABLE void openOnlineHelp(const QString& pHelpSectionName); -}; - -} // namespace governikus diff --git a/src/ui/qml/QmlExtension_android.cpp b/src/ui/qml/QmlExtension_android.cpp deleted file mode 100644 index 2661982..0000000 --- a/src/ui/qml/QmlExtension_android.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "QmlExtension.h" - -#include "LogHandler.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(qml) - - -using namespace governikus; - -void QmlExtension::showSettings(const QString& pAction) -{ - QAndroidJniEnvironment env; - - const QAndroidJniObject& jAction = QAndroidJniObject::fromString(pAction); - QAndroidJniObject intent("android/content/Intent", "(Ljava/lang/String;)V", jAction.object()); - const jint flag = QAndroidJniObject::getStaticField("android/content/Intent", "FLAG_ACTIVITY_NEW_TASK"); - intent.callObjectMethod("setFlags", "(I)V", flag); - - if (intent.isValid()) - { - qCCritical(qml) << "Call action:" << pAction; - QtAndroid::startActivity(intent, 0); - } - - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Cannot call an action as activity:" << pAction; - env->ExceptionDescribe(); - env->ExceptionClear(); - } -} - - -void QmlExtension::showFeedback(const QString& pMessage) -{ - // Wait for toast activation synchronously so that the app can not be deactivated - // in the meantime and all used Java objects are still alive when accessed. - QtAndroid::runOnAndroidThreadSync([pMessage](){ - QAndroidJniEnvironment env; - - const QAndroidJniObject& jMessage = QAndroidJniObject::fromString(pMessage); - const QAndroidJniObject& toast = QAndroidJniObject::callStaticObjectMethod( - "android/widget/Toast", - "makeText", - "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;", - QtAndroid::androidActivity().object(), - jMessage.object(), - jint(1)); - toast.callMethod("show"); - - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Suppressing an unexpected exception."; - env->ExceptionDescribe(); - env->ExceptionClear(); - // The toast was probably not displayed (e.g. DeadObjectException). We halt on error - // since it is used to display information to the user as required by the TR. - Q_ASSERT(false); - } - }); -} - - -bool QmlExtension::exportHistory(const QString&) const -{ - qCWarning(qml) << "NOT IMPLEMENTED"; - return false; -} - - -void QmlExtension::keepScreenOn(bool pActive) -{ - QtAndroid::runOnAndroidThread([pActive](){ - QtAndroid::androidActivity().callMethod("keepScreenOn", "(Z)V", pActive); - QAndroidJniEnvironment env; - if (env->ExceptionCheck()) - { - qCCritical(qml) << "Exception calling java native function."; - env->ExceptionDescribe(); - env->ExceptionClear(); - } - }); -} - - -void QmlExtension::openOnlineHelp(const QString& pHelpSectionName) -{ - Q_UNUSED(pHelpSectionName); - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -#include "moc_QmlExtension.cpp" diff --git a/src/ui/qml/QmlExtension_generic.cpp b/src/ui/qml/QmlExtension_generic.cpp deleted file mode 100644 index 764ba18..0000000 --- a/src/ui/qml/QmlExtension_generic.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "QmlExtension.h" - -#ifndef Q_OS_WINRT -#include "PdfExporter.h" -#endif - -#include "HelpAction.h" - -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(qml) - -using namespace governikus; - - -void QmlExtension::showSettings(const QString&) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -void QmlExtension::showFeedback(const QString&) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -bool QmlExtension::exportHistory(const QString& pPdfUrl) const -{ -#ifdef Q_OS_WINRT - return false; - -#else - PdfExporter exporter(QUrl(pPdfUrl).toLocalFile()); - return exporter.exportHistory(); - -#endif -} - - -void QmlExtension::keepScreenOn(bool) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -void QmlExtension::openOnlineHelp(const QString& pHelpSectionName) -{ - HelpAction::openContextHelp(pHelpSectionName); -} - - -#include "moc_QmlExtension.cpp" diff --git a/src/ui/qml/QmlExtension_ios.mm b/src/ui/qml/QmlExtension_ios.mm deleted file mode 100644 index 68cce36..0000000 --- a/src/ui/qml/QmlExtension_ios.mm +++ /dev/null @@ -1,65 +0,0 @@ -/*! - * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "QmlExtension.h" - -#include -#import - -Q_DECLARE_LOGGING_CATEGORY(qml) - -using namespace governikus; - - -void QmlExtension::showSettings(const QString&) -{ - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -void QmlExtension::showFeedback(const QString& pMessage) -{ - NSString* msg = pMessage.toNSString(); - - UIAlertController* alert = [UIAlertController - alertControllerWithTitle:msg - message:@"" - preferredStyle:UIAlertControllerStyleAlert]; - - UIViewController* rootController = [[UIApplication sharedApplication].keyWindow rootViewController]; - [rootController presentViewController:alert animated:YES completion:nil]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, static_cast(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [alert dismissViewControllerAnimated:YES completion:nil]; - }); -} - - -bool QmlExtension::exportHistory(const QString&) const -{ - qCWarning(qml) << "NOT IMPLEMENTED"; - return false; -} - - -void QmlExtension::keepScreenOn(bool pActive) -{ - if (pActive) - { - [[UIApplication sharedApplication]setIdleTimerDisabled:YES]; - } - else - { - [[UIApplication sharedApplication]setIdleTimerDisabled:NO]; - } -} - - -void QmlExtension::openOnlineHelp(const QString& pHelpSectionName) -{ - Q_UNUSED(pHelpSectionName); - qCWarning(qml) << "NOT IMPLEMENTED"; -} - - -#include "moc_QmlExtension.cpp" diff --git a/src/ui/qml/ReaderScanEnabler.cpp b/src/ui/qml/ReaderScanEnabler.cpp new file mode 100644 index 0000000..669004d --- /dev/null +++ b/src/ui/qml/ReaderScanEnabler.cpp @@ -0,0 +1,61 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "ReaderScanEnabler.h" + +#include "Env.h" +#include "ReaderManager.h" + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(qml) + +using namespace governikus; + + +ReaderScanEnabler::ReaderScanEnabler(QQuickItem* pParent) + : QQuickItem(pParent) + , mObligedToStopScan(false) +{ + if (isVisible()) + { + enableScan(true); + } +} + + +ReaderScanEnabler::~ReaderScanEnabler() +{ + enableScan(false); +} + + +void ReaderScanEnabler::enableScan(const bool pEnable) +{ + const auto manager = Env::getSingleton(); + if (pEnable && !manager->isScanRunning()) + { + qCDebug(qml) << "Starting scan."; + mObligedToStopScan = true; + manager->startScanAll(false); + } + else if (mObligedToStopScan) + { + qCDebug(qml) << "Stopping scan."; + mObligedToStopScan = false; + manager->stopScanAll(); + } +} + + +void ReaderScanEnabler::itemChange(QQuickItem::ItemChange pChange, const QQuickItem::ItemChangeData& pValue) +{ + if (pChange == QQuickItem::ItemVisibleHasChanged) + { + enableScan(pValue.boolValue); + } + + QQuickItem::itemChange(pChange, pValue); +} diff --git a/src/ui/qml/ReaderScanEnabler.h b/src/ui/qml/ReaderScanEnabler.h new file mode 100644 index 0000000..28fd0fc --- /dev/null +++ b/src/ui/qml/ReaderScanEnabler.h @@ -0,0 +1,30 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ + +class ReaderScanEnabler + : public QQuickItem +{ + Q_OBJECT + + private: + bool mObligedToStopScan; + + void enableScan(const bool pEnable); + + public: + explicit ReaderScanEnabler(QQuickItem* pParent = nullptr); + ~ReaderScanEnabler() override; + + void itemChange(QQuickItem::ItemChange pChange, const QQuickItem::ItemChangeData& pValue) override; +}; + + +} // namespace governikus diff --git a/src/ui/qml/RemoteServiceModel.cpp b/src/ui/qml/RemoteServiceModel.cpp index f8e693a..494b3b7 100644 --- a/src/ui/qml/RemoteServiceModel.cpp +++ b/src/ui/qml/RemoteServiceModel.cpp @@ -5,7 +5,6 @@ #include "RemoteServiceModel.h" #include "AppSettings.h" -#include "Env.h" #include "EstablishPaceChannelParser.h" #include "NumberModel.h" #include "RemoteClientImpl.h" @@ -27,7 +26,8 @@ RemoteServiceModel::RemoteServiceModel() , mPsk() , mAvailableRemoteDevices(this, false, true) , mKnownDevices(this, true, false) - , mConnectedClientDeviceName() + , mCombinedDevices(this, true, true) + , mConnectionInfo() , mConnectedServerDeviceNames() , mIsSaCPinChangeWorkflow() , mRememberedServerEntry() @@ -39,7 +39,7 @@ RemoteServiceModel::RemoteServiceModel() connect(readerManager, &ReaderManager::fireReaderRemoved, this, &RemoteServiceModel::onEnvironmentChanged); connect(&mWifiInfo, &WifiInfo::fireWifiEnabledChanged, this, &RemoteServiceModel::onEnvironmentChanged); - RemoteClient* const remoteClient = Env::getSingleton(); + auto* const remoteClient = Env::getSingleton(); connect(remoteClient, &RemoteClient::fireDetectionChanged, this, &RemoteServiceModel::fireDetectionChanged); connect(remoteClient, &RemoteClient::fireNewRemoteDispatcher, this, &RemoteServiceModel::onConnectedDevicesChanged); connect(remoteClient, &RemoteClient::fireDispatcherDestroyed, this, &RemoteServiceModel::onConnectedDevicesChanged); @@ -64,7 +64,7 @@ void RemoteServiceModel::onEnvironmentChanged() nfcPluginEnabled |= pluginInfo.isEnabled(); } - bool readerAvailable = (Env::getSingleton()->getReaderInfos().length() > 0); + bool readerAvailable = !(Env::getSingleton()->getReaderInfos(ReaderManagerPlugInType::NFC).isEmpty()); const bool wifiEnabled = mWifiInfo.isWifiEnabled(); const bool runnable = readerAvailable && wifiEnabled; @@ -124,17 +124,28 @@ RemoteDeviceModel* RemoteServiceModel::getKnownDevices() } +RemoteDeviceModel* RemoteServiceModel::getCombinedDevices() +{ + return &mCombinedDevices; +} + + void RemoteServiceModel::setDetectRemoteDevices(bool pNewStatus) { + if (pNewStatus == Env::getSingleton()->isDetecting()) + { + return; + } + if (pNewStatus) { - mAvailableRemoteDevices.onWidgetShown(); - mKnownDevices.onWidgetShown(); + mAvailableRemoteDevices.onUiShown(); + mKnownDevices.onUiShown(); } else { - mAvailableRemoteDevices.onWidgetHidden(); - mKnownDevices.onWidgetHidden(); + mAvailableRemoteDevices.onUiHidden(); + mKnownDevices.onUiHidden(); } } @@ -149,7 +160,7 @@ void RemoteServiceModel::connectToRememberedServer(const QString& pServerPsk) { if (!pServerPsk.isEmpty() && !mRememberedServerEntry.isNull()) { - RemoteClient* const remoteClient = Env::getSingleton(); + auto* const remoteClient = Env::getSingleton(); connect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &RemoteServiceModel::onEstablishConnectionDone); qDebug() << "Starting to pair."; @@ -167,8 +178,8 @@ bool RemoteServiceModel::rememberServer(const QString& pDeviceId) void RemoteServiceModel::onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus) { - Q_UNUSED(pEntry); - RemoteClient* const remoteClient = Env::getSingleton(); + Q_UNUSED(pEntry) + auto* const remoteClient = Env::getSingleton(); disconnect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &RemoteServiceModel::onEstablishConnectionDone); qDebug() << "Pairing finished:" << pStatus; if (pStatus.isError()) @@ -178,16 +189,26 @@ void RemoteServiceModel::onEstablishConnectionDone(const QSharedPointer()->getRemoteServiceSettings(); - const QString peerName = settings.getRemoteInfo(getCurrentFingerprint()).getName(); - mConnectedClientDeviceName = peerName; - Q_EMIT fireConnectedClientDeviceNameChanged(); + if (pConnected) + { + const RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); + const QString peerName = settings.getRemoteInfo(getCurrentFingerprint()).getName(); + //: INFO ANDROID IOS The smartphone is connected as card reader (SaK) and currently processing an authentication request. The user is asked to pay attention the its screen. + mConnectionInfo = tr("Please pay attention to the display on your other device \"%1\".").arg(peerName); + Q_EMIT fireConnectionInfoChanged(); + } Q_EMIT fireConnectedChanged(pConnected); } +void RemoteServiceModel::onCardConnectionEstablished(const QSharedPointer& pConnection) +{ + pConnection->setProgressMessage(mConnectionInfo); +} + + void RemoteServiceModel::resetContext(const QSharedPointer& pContext) { mContext = pContext; @@ -196,7 +217,6 @@ void RemoteServiceModel::resetContext(const QSharedPointer mPsk.clear(); onEstablishPaceChannelMessageUpdated(QSharedPointer()); - mContext = pContext; if (mContext) { connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &RemoteServiceModel::fireIsRunningChanged); @@ -204,7 +224,8 @@ void RemoteServiceModel::resetContext(const QSharedPointer mPsk = pPsk; }); connect(mContext->getRemoteServer().data(), &RemoteServer::firePskChanged, this, &RemoteServiceModel::firePskChanged); - connect(mContext->getRemoteServer().data(), &RemoteServer::fireConnectedChanged, this, &RemoteServiceModel::onClientConnectedChanged); + connect(mContext->getRemoteServer().data(), &RemoteServer::fireConnectedChanged, this, &RemoteServiceModel::onConnectionInfoChanged); + connect(mContext.data(), &RemoteServiceContext::fireCardConnectionEstablished, this, &RemoteServiceModel::onCardConnectionEstablished); connect(mContext.data(), &RemoteServiceContext::fireEstablishPaceChannelMessageUpdated, this, &RemoteServiceModel::onEstablishPaceChannelMessageUpdated); } @@ -249,13 +270,49 @@ bool RemoteServiceModel::isSaCPinChangeWorkflow() const } +bool RemoteServiceModel::isRunnable() const +{ + return mRunnable; +} + + +bool RemoteServiceModel::isCanEnableNfc() const +{ + return mCanEnableNfc; +} + + +QString RemoteServiceModel::getErrorMessage() const +{ + return mErrorMessage; +} + + +QByteArray RemoteServiceModel::getPsk() const +{ + return mPsk; +} + + +QString RemoteServiceModel::getConnectionInfo() const +{ + return mConnectionInfo; +} + + +QString RemoteServiceModel::getConnectedServerDeviceNames() const +{ + return mConnectedServerDeviceNames; +} + + bool RemoteServiceModel::pinPadModeOn() { return Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); } -QString RemoteServiceModel::getPacePasswordId() const +QString RemoteServiceModel::getPasswordType() const { if (mContext.isNull()) { @@ -290,14 +347,17 @@ QString RemoteServiceModel::getErrorMessage(bool pNfcPluginAvailable, bool pNfcP { if (!pNfcPluginAvailable) { + //: INFO ALL_PLATFORMS The device does not offer NFC. return tr("NFC is not available on your device."); } if (!pNfcPluginEnabled) { + //: INFO ALL_PLATFORMS NFC is available but not active. return tr("Please enable NFC to use the remote service."); } if (!pWifiEnabled) { + //: INFO ALL_PLATFORMS The wifi feature is not enabled but required to use the smartphone as a card reader (SaK). return tr("Please connect your WiFi to use the remote service."); } @@ -328,7 +388,7 @@ RemoteServiceModel& RemoteServiceModel::getInstance() void RemoteServiceModel::onConnectedDevicesChanged() { - RemoteClient* const remoteClient = Env::getSingleton(); + auto* const remoteClient = Env::getSingleton(); const auto deviceInfos = remoteClient->getConnectedDeviceInfos(); QStringList deviceNames; for (const auto& info : deviceInfos) diff --git a/src/ui/qml/RemoteServiceModel.h b/src/ui/qml/RemoteServiceModel.h index fffcb51..a4242ee 100644 --- a/src/ui/qml/RemoteServiceModel.h +++ b/src/ui/qml/RemoteServiceModel.h @@ -7,6 +7,7 @@ #pragma once #include "context/RemoteServiceContext.h" +#include "Env.h" #include "ReaderManager.h" #include "RemoteDeviceModel.h" #include "WifiInfo.h" @@ -15,8 +16,6 @@ #include #include -class test_RemoteServiceModel; - namespace governikus { @@ -24,24 +23,24 @@ class RemoteServiceModel : public WorkflowModel { Q_OBJECT + friend class Env; Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY fireIsRunningChanged) - Q_PROPERTY(bool runnable MEMBER mRunnable NOTIFY fireEnvironmentChanged) - Q_PROPERTY(bool canEnableNfc MEMBER mCanEnableNfc NOTIFY fireEnvironmentChanged) - Q_PROPERTY(QString errorMessage MEMBER mErrorMessage NOTIFY fireEnvironmentChanged) - Q_PROPERTY(QByteArray psk MEMBER mPsk NOTIFY firePskChanged) + Q_PROPERTY(bool runnable READ isRunnable NOTIFY fireEnvironmentChanged) + Q_PROPERTY(bool canEnableNfc READ isCanEnableNfc NOTIFY fireEnvironmentChanged) + Q_PROPERTY(QString errorMessage READ getErrorMessage NOTIFY fireEnvironmentChanged) + Q_PROPERTY(QByteArray psk READ getPsk NOTIFY firePskChanged) Q_PROPERTY(QString currentFingerprint READ getCurrentFingerprint NOTIFY fireConnectedChanged) Q_PROPERTY(bool connected READ isConnected NOTIFY fireConnectedChanged) - Q_PROPERTY(QString connectedClientDeviceName MEMBER mConnectedClientDeviceName NOTIFY fireConnectedClientDeviceNameChanged) - Q_PROPERTY(QString connectedServerDeviceNames MEMBER mConnectedServerDeviceNames NOTIFY fireConnectedServerDeviceNamesChanged) + Q_PROPERTY(QString connectionInfo READ getConnectionInfo NOTIFY fireConnectionInfoChanged) + Q_PROPERTY(QString connectedServerDeviceNames READ getConnectedServerDeviceNames NOTIFY fireConnectedServerDeviceNamesChanged) Q_PROPERTY(RemoteDeviceModel * availableRemoteDevices READ getAvailableRemoteDevices CONSTANT) Q_PROPERTY(RemoteDeviceModel * knownDevices READ getKnownDevices CONSTANT) + Q_PROPERTY(RemoteDeviceModel * combinedDevices READ getCombinedDevices CONSTANT) Q_PROPERTY(bool detectRemoteDevices READ detectRemoteDevices WRITE setDetectRemoteDevices NOTIFY fireDetectionChanged) Q_PROPERTY(bool isSaCPinChangeWorkflow READ isSaCPinChangeWorkflow NOTIFY fireEstablishPaceChannelMessageUpdated) private: - friend class ::test_RemoteServiceModel; - QSharedPointer mContext; WifiInfo mWifiInfo; bool mRunnable; @@ -50,7 +49,8 @@ class RemoteServiceModel QByteArray mPsk; RemoteDeviceModel mAvailableRemoteDevices; RemoteDeviceModel mKnownDevices; - QString mConnectedClientDeviceName; + RemoteDeviceModel mCombinedDevices; + QString mConnectionInfo; QString mConnectedServerDeviceNames; bool mIsSaCPinChangeWorkflow; QSharedPointer mRememberedServerEntry; @@ -60,13 +60,15 @@ class RemoteServiceModel private Q_SLOTS: void onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus); - void onClientConnectedChanged(bool pConnected); + void onConnectionInfoChanged(bool pConnected); + void onCardConnectionEstablished(const QSharedPointer& pConnection); void onConnectedDevicesChanged(); void onEstablishPaceChannelMessageUpdated(const QSharedPointer& pMessage); protected: RemoteServiceModel(); ~RemoteServiceModel() override = default; + static RemoteServiceModel& getInstance(); public: bool isRunning() const; @@ -74,6 +76,7 @@ class RemoteServiceModel RemoteDeviceModel* getAvailableRemoteDevices(); RemoteDeviceModel* getKnownDevices(); + RemoteDeviceModel* getCombinedDevices(); void setDetectRemoteDevices(bool pNewStatus); bool detectRemoteDevices(); Q_INVOKABLE bool rememberServer(const QString& pDeviceId); @@ -84,14 +87,18 @@ class RemoteServiceModel QString getCurrentFingerprint() const; bool isConnected() const; bool isSaCPinChangeWorkflow() const; + bool isRunnable() const; + bool isCanEnableNfc() const; + QString getErrorMessage() const; + QByteArray getPsk() const; + QString getConnectionInfo() const; + QString getConnectedServerDeviceNames() const; Q_INVOKABLE bool pinPadModeOn(); - Q_INVOKABLE QString getPacePasswordId() const; + Q_INVOKABLE QString getPasswordType() const; Q_INVOKABLE void forgetDevice(const QString& pId); Q_INVOKABLE void cancelPasswordRequest(); - static RemoteServiceModel& getInstance(); - Q_SIGNALS: void fireIsRunningChanged(); void fireEnvironmentChanged(); @@ -100,7 +107,7 @@ class RemoteServiceModel void fireServerPskChanged(); void fireDetectionChanged(); void firePairingFailed(); - void fireConnectedClientDeviceNameChanged(); + void fireConnectionInfoChanged(); void fireConnectedServerDeviceNamesChanged(); void fireEstablishPaceChannelMessageUpdated(); }; diff --git a/src/ui/qml/SelfAuthModel.cpp b/src/ui/qml/SelfAuthModel.cpp index d5d57d7..12d7810 100644 --- a/src/ui/qml/SelfAuthModel.cpp +++ b/src/ui/qml/SelfAuthModel.cpp @@ -5,11 +5,14 @@ #include "SelfAuthModel.h" #include "context/SelfAuthContext.h" -#include "LanguageLoader.h" +#include "SingletonHelper.h" using namespace governikus; +defineSingleton(SelfAuthModel) + + void SelfAuthModel::onSelfAuthenticationDataChanged() { beginResetModel(); @@ -46,6 +49,12 @@ SelfAuthModel::SelfAuthModel(QObject* pParent) } +SelfAuthModel& SelfAuthModel::getInstance() +{ + return *Instance; +} + + void SelfAuthModel::resetContext(const QSharedPointer& pContext) { mContext = pContext; diff --git a/src/ui/qml/SelfAuthModel.h b/src/ui/qml/SelfAuthModel.h index 254d0d5..a6f39b3 100644 --- a/src/ui/qml/SelfAuthModel.h +++ b/src/ui/qml/SelfAuthModel.h @@ -6,6 +6,7 @@ #pragma once +#include "Env.h" #include "SelfAuthenticationData.h" #include @@ -13,8 +14,6 @@ #include #include -class test_SelfAuthModel; - namespace governikus { @@ -24,22 +23,25 @@ class SelfAuthModel : public QAbstractListModel { Q_OBJECT + friend class Env; QSharedPointer mContext; SelfAuthenticationData::OrderedSelfData mSelfData; - enum DataRoles - { - NAME = Qt::UserRole + 1, - VALUE - }; - private Q_SLOTS: - friend class ::test_SelfAuthModel; void onSelfAuthenticationDataChanged(); - public: + protected: SelfAuthModel(QObject* pParent = nullptr); + static SelfAuthModel& getInstance(); + + public: + enum DataRoles + { + NAME = Qt::UserRole + 1, + VALUE + }; + void resetContext(const QSharedPointer& pContext = QSharedPointer()); Q_INVOKABLE void startWorkflow(); diff --git a/src/ui/qml/SelfDiagnosisModel.cpp b/src/ui/qml/SelfDiagnosisModel.cpp new file mode 100644 index 0000000..75be0f8 --- /dev/null +++ b/src/ui/qml/SelfDiagnosisModel.cpp @@ -0,0 +1,81 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "SelfDiagnosisModel.h" + +#include "AppSettings.h" +#include "GeneralSettings.h" +#include "SingletonHelper.h" + +#include +#include +#include + + +using namespace governikus; + +defineSingleton(SelfDiagnosisModel) + + +SelfDiagnosisModel::SelfDiagnosisModel(QObject* pParent) + : QObject(pParent) + , mDiagnosisContext(new DiagnosisContext) + , mDiagnosisModel(mDiagnosisContext) +{ + const GeneralSettings& generalSettings = Env::getSingleton()->getGeneralSettings(); + connect(&generalSettings, &GeneralSettings::fireLanguageChanged, this, &SelfDiagnosisModel::fireSectionContentModelChanged); +} + + +void SelfDiagnosisModel::saveToFile(const QUrl& pFilename) const +{ + QFile file(pFilename.toLocalFile()); + if (file.open(QIODevice::WriteOnly)) + { + QString diagnosisLog = mDiagnosisModel.getAsPlaintext(); + file.write(diagnosisLog.toUtf8()); + } +} + + +QString SelfDiagnosisModel::getCreationTimeString() const +{ + return mDiagnosisModel.getCreationTimeString(); +} + + +SelfDiagnosisModel& SelfDiagnosisModel::getInstance() +{ + return *Instance; +} + + +QAbstractListModel* SelfDiagnosisModel::getSectionsModel() +{ + return &mDiagnosisModel; +} + + +QAbstractListModel* SelfDiagnosisModel::getSectionContentModel(const QString& pSection) +{ + const auto model = mDiagnosisModel.getSectionContent(pSection); + QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership); + return model; +} + + +void SelfDiagnosisModel::startController() +{ + if (mDiagnosisController.isNull()) + { + mDiagnosisController.reset(new DiagnosisController(mDiagnosisContext)); + mDiagnosisController->run(); + } +} + + +void SelfDiagnosisModel::stopController() +{ + mDiagnosisController.reset(); +} diff --git a/src/ui/qml/SelfDiagnosisModel.h b/src/ui/qml/SelfDiagnosisModel.h new file mode 100644 index 0000000..12b6070 --- /dev/null +++ b/src/ui/qml/SelfDiagnosisModel.h @@ -0,0 +1,51 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "context/DiagnosisContext.h" +#include "controller/DiagnosisController.h" +#include "DiagnosisModel.h" +#include "Env.h" + +#include +#include +#include +#include + + +namespace governikus +{ + +class SelfDiagnosisModel + : public QObject +{ + Q_OBJECT + friend class Env; + + Q_PROPERTY(QAbstractListModel * sectionsModel READ getSectionsModel CONSTANT) + + private: + QSharedPointer mDiagnosisContext; + DiagnosisModel mDiagnosisModel; + QScopedPointer mDiagnosisController; + + protected: + explicit SelfDiagnosisModel(QObject* pParent = nullptr); + static SelfDiagnosisModel& getInstance(); + + public: + QAbstractListModel* getSectionsModel(); + Q_INVOKABLE QAbstractListModel* getSectionContentModel(const QString& pSection); + Q_INVOKABLE void startController(); + Q_INVOKABLE void stopController(); + Q_INVOKABLE void saveToFile(const QUrl& pFilename) const; + Q_INVOKABLE QString getCreationTimeString() const; + + Q_SIGNALS: + void fireSectionContentModelChanged(); + void fireCurrentSectionChanged(); +}; + +} // namespace governikus diff --git a/src/ui/qml/SettingsModel.cpp b/src/ui/qml/SettingsModel.cpp index a61e662..3ccd326 100644 --- a/src/ui/qml/SettingsModel.cpp +++ b/src/ui/qml/SettingsModel.cpp @@ -7,6 +7,10 @@ #include "AppSettings.h" #include "HistorySettings.h" #include "LanguageLoader.h" +#include "PlatformHelper.h" +#include "Service.h" +#include "SingletonHelper.h" + #ifdef Q_OS_ANDROID #include @@ -15,30 +19,33 @@ using namespace governikus; +defineSingleton(SettingsModel) + + SettingsModel::SettingsModel() - : mShowTutorialOnStart(false) + : QObject() + , mIsStartedByAuth(false) { const HistorySettings& settings = Env::getSingleton()->getHistorySettings(); connect(&settings, &HistorySettings::fireEnabledChanged, this, &SettingsModel::fireHistoryEnabledChanged); + const auto* service = Env::getSingleton(); + const auto* dataModel = Env::getSingleton(); + connect(service, &Service::fireAppUpdateFinished, dataModel, &AppUpdateDataModel::onAppUpdateFinished); + connect(service, &Service::fireAppUpdateFinished, this, &SettingsModel::fireAppUpdateDataChanged); #ifdef Q_OS_ANDROID - auto& generalSettings = Env::getSingleton()->getGeneralSettings(); - if (generalSettings.isShowSetupAssistant()) - { - generalSettings.setShowSetupAssistant(false); - generalSettings.save(); - - bool startedByAuth = QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/MainActivity", "isStartedByAuth"); - if (!startedByAuth) - { - mShowTutorialOnStart = true; - } - } + mIsStartedByAuth = QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/MainActivity", "isStartedByAuth"); #endif } +SettingsModel& SettingsModel::getInstance() +{ + return *Instance; +} + + QString SettingsModel::getEmptyString() { return QString(); @@ -53,11 +60,13 @@ QString SettingsModel::getLanguage() const void SettingsModel::setLanguage(const QString& pLanguage) { - GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); - settings.setLanguage(QLocale(pLanguage).language()); - settings.save(); - - Q_EMIT fireLanguageChanged(); + if (getLanguage() != pLanguage) + { + GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); + settings.setLanguage(QLocale(pLanguage).language()); + settings.save(); + Q_EMIT fireLanguageChanged(); + } } @@ -69,14 +78,13 @@ bool SettingsModel::isDeveloperMode() const void SettingsModel::setDeveloperMode(bool pEnable) { - if (isDeveloperMode() == pEnable) + if (isDeveloperMode() != pEnable) { - return; + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setDeveloperMode(pEnable); + settings.save(); + Q_EMIT fireDeveloperModeChanged(); } - - Env::getSingleton()->getGeneralSettings().setDeveloperMode(pEnable); - Env::getSingleton()->getGeneralSettings().save(); - Q_EMIT fireDeveloperModeChanged(); } @@ -88,14 +96,13 @@ bool SettingsModel::useSelfauthenticationTestUri() const void SettingsModel::setUseSelfauthenticationTestUri(bool pUse) { - if (useSelfauthenticationTestUri() == pUse) + if (useSelfauthenticationTestUri() != pUse) { - return; + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setUseSelfauthenticationTestUri(pUse); + settings.save(); + Q_EMIT fireUseSelfauthenticationTestUriChanged(); } - - Env::getSingleton()->getGeneralSettings().setUseSelfauthenticationTestUri(pUse); - Env::getSingleton()->getGeneralSettings().save(); - Q_EMIT fireUseSelfauthenticationTestUriChanged(); } @@ -125,6 +132,15 @@ void SettingsModel::removeTrustedCertificate(const QString& pFingerprint) } +int SettingsModel::removeHistory(const QString& pPeriodToRemove) +{ + HistorySettings& settings = Env::getSingleton()->getHistorySettings(); + int removedItemCount = settings.deleteSettings(Enum::fromString(pPeriodToRemove, TimePeriod::UNKNOWN)); + settings.save(); + return removedItemCount; +} + + bool SettingsModel::getPinPadMode() const { return Env::getSingleton()->getRemoteServiceSettings().getPinPadMode(); @@ -148,23 +164,127 @@ bool SettingsModel::isHistoryEnabled() const void SettingsModel::setHistoryEnabled(bool pEnabled) { - HistorySettings& settings = Env::getSingleton()->getHistorySettings(); - settings.setEnabled(pEnabled); - settings.save(); + if (isHistoryEnabled() != pEnabled) + { + HistorySettings& settings = Env::getSingleton()->getHistorySettings(); + settings.setEnabled(pEnabled); + settings.save(); + } } -int SettingsModel::removeHistory(const QString& pPeriodToRemove) +int SettingsModel::removeEntireHistory() { HistorySettings& settings = Env::getSingleton()->getHistorySettings(); - int removedItemCount = settings.deleteSettings(Enum::fromString(pPeriodToRemove, TimePeriod::UNKNOWN)); - settings.save(); - return removedItemCount; + return settings.deleteSettings(TimePeriod::ALL_HISTORY); +} + + +bool SettingsModel::isUseScreenKeyboard() const +{ + return Env::getSingleton()->getGeneralSettings().isUseScreenKeyboard(); +} + + +void SettingsModel::setUseScreenKeyboard(bool pUseScreenKeyboard) +{ + if (isUseScreenKeyboard() != pUseScreenKeyboard) + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setUseScreenKeyboard(pUseScreenKeyboard); + settings.save(); + Q_EMIT fireScreenKeyboardChanged(); + } +} + + +bool SettingsModel::isShuffleScreenKeyboard() const +{ + return Env::getSingleton()->getGeneralSettings().isShuffleScreenKeyboard(); +} + + +void SettingsModel::setShuffleScreenKeyboard(bool pShuffleScreenKeyboard) +{ + if (isShuffleScreenKeyboard() != pShuffleScreenKeyboard) + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setShuffleScreenKeyboard(pShuffleScreenKeyboard); + settings.save(); + Q_EMIT fireScreenKeyboardChanged(); + } +} + + +bool SettingsModel::isShowSetupAssistantOnStart() const +{ + auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + return generalSettings.isShowSetupAssistant() && !mIsStartedByAuth; +} + + +void SettingsModel::setShowSetupAssistantOnStart(bool pShowSetupAssistantOnStart) +{ + if (isShowSetupAssistantOnStart() != pShowSetupAssistantOnStart) + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setShowSetupAssistant(pShowSetupAssistantOnStart); + settings.save(); + Q_EMIT fireShowSetupAssistantOnStartChanged(); + } +} + + +bool SettingsModel::isAutoStart() const +{ + return Env::getSingleton()->getGeneralSettings().isAutoStart(); +} + + +bool SettingsModel::autoStartIsSetByAdmin() const +{ + return Env::getSingleton()->getGeneralSettings().autoStartIsSetByAdmin(); +} + + +void SettingsModel::setAutoStart(bool pEnabled) +{ + if (isAutoStart() != pEnabled) + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setAutoStart(pEnabled); + settings.save(); + Q_EMIT fireAutoStartChanged(); + } +} + + +bool SettingsModel::requestStoreFeedback() const +{ +#ifdef Q_OS_ANDROID + const bool startedByAuth = QAndroidJniObject::callStaticMethod("com/governikus/ausweisapp2/MainActivity", "isStartedByAuth"); + if (startedByAuth) + { + return false; + } +#endif + + return Env::getSingleton()->getGeneralSettings().isRequestStoreFeedback(); +} + + +void SettingsModel::hideFutureStoreFeedbackDialogs() +{ + Env::getSingleton()->getGeneralSettings().setRequestStoreFeedback(false); } bool SettingsModel::askForDeviceSurvey() const { + if (isPlatform(*this, Platform::IOS)) + { + return false; + } return Env::getSingleton()->getGeneralSettings().askForDeviceSurvey(); } @@ -173,3 +293,96 @@ void SettingsModel::setDeviceSurveyPending(bool pDeviceSurveyPending) { Env::getSingleton()->getGeneralSettings().setDeviceSurveyPending(pDeviceSurveyPending); } + + +bool SettingsModel::isAutoCloseWindowAfterAuthentication() const +{ + return Env::getSingleton()->getGeneralSettings().isAutoCloseWindowAfterAuthentication(); +} + + +void SettingsModel::setAutoCloseWindowAfterAuthentication(bool pEnabled) +{ + if (isAutoCloseWindowAfterAuthentication() != pEnabled) + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setAutoCloseWindowAfterAuthentication(pEnabled); + settings.save(); + Q_EMIT fireAutoCloseWindowAfterAuthenticationChanged(); + } +} + + +bool SettingsModel::isAutoUpdateCheck() const +{ + return Env::getSingleton()->getGeneralSettings().isAutoUpdateCheck(); +} + + +bool SettingsModel::autoUpdateCheckIsSetByAdmin() const +{ + return Env::getSingleton()->getGeneralSettings().autoUpdateCheckIsSetByAdmin(); +} + + +void SettingsModel::setAutoUpdateCheck(bool pAutoUpdateCheck) +{ + if (isAutoUpdateCheck() != pAutoUpdateCheck) + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setAutoUpdateCheck(pAutoUpdateCheck); + settings.save(); + Q_EMIT fireAutoUpdateCheckChanged(); + } +} + + +bool SettingsModel::isRemindUserToClose() const +{ + return Env::getSingleton()->getGeneralSettings().isRemindUserToClose(); +} + + +void SettingsModel::setRemindUserToClose(bool pRemindUser) +{ + if (isRemindUserToClose() != pRemindUser) + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setRemindUserToClose(pRemindUser); + settings.save(); + Q_EMIT fireRemindUserToCloseChanged(); + } +} + + +bool SettingsModel::isShowInAppNotifications() const +{ + + return Env::getSingleton()->getGeneralSettings().isShowInAppNotifications(); +} + + +void SettingsModel::setShowInAppNotifications(bool pShowInAppNotifications) +{ + if (isShowInAppNotifications() != pShowInAppNotifications) + { + + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setShowInAppNotifications(pShowInAppNotifications); + settings.save(); + Q_EMIT fireShowInAppNotificationsChanged(); + } +} + + +void SettingsModel::updateApp() +{ + Env::getSingleton()->updateApp(true); +} + + +AppUpdateDataModel* SettingsModel::getAppUpdateData() const +{ + auto* dataModel = Env::getSingleton(); + return dataModel; +} diff --git a/src/ui/qml/SettingsModel.h b/src/ui/qml/SettingsModel.h index 95662f5..e858e9e 100644 --- a/src/ui/qml/SettingsModel.h +++ b/src/ui/qml/SettingsModel.h @@ -6,6 +6,9 @@ #pragma once +#include "AppUpdateDataModel.h" +#include "Env.h" + #include namespace governikus @@ -15,6 +18,8 @@ class SettingsModel : public QObject { Q_OBJECT + friend class Env; + Q_PROPERTY(QString translationTrigger READ getEmptyString NOTIFY fireLanguageChanged) Q_PROPERTY(QString language READ getLanguage WRITE setLanguage NOTIFY fireLanguageChanged) Q_PROPERTY(bool developerMode READ isDeveloperMode WRITE setDeveloperMode NOTIFY fireDeveloperModeChanged) @@ -22,14 +27,27 @@ class SettingsModel Q_PROPERTY(bool pinPadMode READ getPinPadMode WRITE setPinPadMode NOTIFY firePinPadModeChanged) Q_PROPERTY(QString serverName READ getServerName WRITE setServerName NOTIFY fireDeviceNameChanged) Q_PROPERTY(bool historyEnabled READ isHistoryEnabled WRITE setHistoryEnabled NOTIFY fireHistoryEnabledChanged) - Q_PROPERTY(bool showTutorialOnStart MEMBER mShowTutorialOnStart CONSTANT) + Q_PROPERTY(bool useScreenKeyboard READ isUseScreenKeyboard WRITE setUseScreenKeyboard NOTIFY fireScreenKeyboardChanged) + Q_PROPERTY(bool shuffleScreenKeyboard READ isShuffleScreenKeyboard WRITE setShuffleScreenKeyboard NOTIFY fireScreenKeyboardChanged) + Q_PROPERTY(bool showSetupAssistantOnStart READ isShowSetupAssistantOnStart WRITE setShowSetupAssistantOnStart NOTIFY fireShowSetupAssistantOnStartChanged) + Q_PROPERTY(bool autoStartApp READ isAutoStart WRITE setAutoStart NOTIFY fireAutoStartChanged) + Q_PROPERTY(bool autoStartSetByAdmin READ autoStartIsSetByAdmin CONSTANT) + Q_PROPERTY(bool autoCloseWindowAfterAuthentication READ isAutoCloseWindowAfterAuthentication WRITE setAutoCloseWindowAfterAuthentication NOTIFY fireAutoCloseWindowAfterAuthenticationChanged) + Q_PROPERTY(bool autoUpdateCheck READ isAutoUpdateCheck WRITE setAutoUpdateCheck NOTIFY fireAutoUpdateCheckChanged) + Q_PROPERTY(bool autoUpdateCheckSetByAdmin READ autoUpdateCheckIsSetByAdmin CONSTANT) + Q_PROPERTY(bool remindUserToClose READ isRemindUserToClose WRITE setRemindUserToClose NOTIFY fireRemindUserToCloseChanged) + Q_PROPERTY(bool showInAppNotifications READ isShowInAppNotifications WRITE setShowInAppNotifications NOTIFY fireShowInAppNotificationsChanged) + Q_PROPERTY(AppUpdateDataModel * appUpdateData READ getAppUpdateData NOTIFY fireAppUpdateDataChanged) private: - bool mShowTutorialOnStart; + bool mIsStartedByAuth; + + protected: + SettingsModel(); + ~SettingsModel() override = default; + static SettingsModel& getInstance(); public: - SettingsModel(); - QString getEmptyString(); QString getLanguage() const; void setLanguage(const QString& pLanguage); @@ -46,6 +64,7 @@ class SettingsModel Q_INVOKABLE void removeTrustedCertificate(const QString& pFingerprint); Q_INVOKABLE int removeHistory(const QString& pPeriodToRemove); + Q_INVOKABLE int removeEntireHistory(); bool getPinPadMode() const; void setPinPadMode(bool pPinPadMode); @@ -53,9 +72,42 @@ class SettingsModel bool isHistoryEnabled() const; void setHistoryEnabled(bool pEnabled); + bool isUseScreenKeyboard() const; + void setUseScreenKeyboard(bool pUseScreenKeyboard); + + bool isShuffleScreenKeyboard() const; + void setShuffleScreenKeyboard(bool pShuffleScreenKeyboard); + + bool isShowSetupAssistantOnStart() const; + void setShowSetupAssistantOnStart(bool pShowSetupAssistantOnStart); + + bool isAutoStart() const; + bool autoStartIsSetByAdmin() const; + void setAutoStart(bool pEnabled); + + bool isAutoCloseWindowAfterAuthentication() const; + void setAutoCloseWindowAfterAuthentication(bool pEnabled); + + bool isAutoUpdateCheck() const; + bool autoUpdateCheckIsSetByAdmin() const; + void setAutoUpdateCheck(bool pAutoUpdateCheck); + + bool isRemindUserToClose() const; + void setRemindUserToClose(bool pRemindUser); + + bool isShowInAppNotifications() const; + void setShowInAppNotifications(bool pShowInAppNotifications); + + Q_INVOKABLE bool requestStoreFeedback() const; + Q_INVOKABLE void hideFutureStoreFeedbackDialogs(); + Q_INVOKABLE bool askForDeviceSurvey() const; Q_INVOKABLE void setDeviceSurveyPending(bool pDeviceSurveyPending); + Q_INVOKABLE void updateApp(); + + AppUpdateDataModel* getAppUpdateData() const; + Q_SIGNALS: void fireLanguageChanged(); void fireDeveloperModeChanged(); @@ -63,6 +115,15 @@ class SettingsModel void fireDeviceNameChanged(); void firePinPadModeChanged(); void fireHistoryEnabledChanged(); + void fireScreenKeyboardChanged(); + void fireShowSetupAssistantOnStartChanged(); + void fireAutoStartChanged(); + void fireAutoCloseWindowAfterAuthenticationChanged(); + void fireAutoUpdateCheckChanged(); + void fireRemindUserToCloseChanged(); + void fireAppUpdateDataChanged(bool pUpdateAvailable, const GlobalStatus& pStatus); + void fireShowInAppNotificationsChanged(); + }; } // namespace governikus diff --git a/src/ui/qml/StatusBarUtil.cpp b/src/ui/qml/StatusBarUtil.cpp deleted file mode 100644 index 2e44c9b..0000000 --- a/src/ui/qml/StatusBarUtil.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/*! - * \brief Utility for changing the color of the status bar. - * - * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "StatusBarUtil.h" - -#include -#include -#include -#ifdef Q_OS_ANDROID - #include - #include - #include - #define FLAG_TRANSLUCENT_STATUS 0x04000000 - #define FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 0x80000000 - #define HSV_VALUE_REDUCTION (255 * 20 / 100) -#endif - - -using namespace governikus; - - -bool StatusBarUtil::catchJavaExceptions() const -{ -#ifdef Q_OS_ANDROID - QAndroidJniEnvironment env; - if (env->ExceptionCheck()) - { - env->ExceptionDescribe(); - env->ExceptionClear(); - qWarning() << "Exception occurred while setting status bar color"; - return true; - } -#endif - return false; -} - - -void StatusBarUtil::setStatusBarColor(const QString& pColor) -{ -#ifdef Q_OS_ANDROID - if (QtAndroid::androidSdkVersion() < 21) - { - return; - } - - QColor color(pColor); - int newValue = color.value() >= HSV_VALUE_REDUCTION ? color.value() - HSV_VALUE_REDUCTION : 0; - color.setHsv(color.hue(), color.saturation(), newValue); - - QtAndroid::runOnAndroidThread([ = ](){ - QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;"); - if (catchJavaExceptions()) - { - return; - } - - window.callMethod("addFlags", "(I)V", FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - window.callMethod("clearFlags", "(I)V", FLAG_TRANSLUCENT_STATUS); - window.callMethod("setStatusBarColor", "(I)V", color.rgba()); - catchJavaExceptions(); - }); - -#else - Q_UNUSED(pColor) -#endif -} diff --git a/src/ui/qml/StatusBarUtil.h b/src/ui/qml/StatusBarUtil.h deleted file mode 100644 index 75035e1..0000000 --- a/src/ui/qml/StatusBarUtil.h +++ /dev/null @@ -1,31 +0,0 @@ -/*! - * \brief Utility for changing the color of the status bar. - * - * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include - -namespace governikus -{ - -class StatusBarUtil - : public QObject -{ - Q_OBJECT - - private: - bool catchJavaExceptions() const; - - public: - StatusBarUtil() = default; - ~StatusBarUtil() override = default; - - Q_INVOKABLE void setStatusBarColor(const QString& pColor); -}; - -} // namespace governikus diff --git a/src/ui/qml/UIPlugInQml.cpp b/src/ui/qml/UIPlugInQml.cpp index 65546a1..539b691 100644 --- a/src/ui/qml/UIPlugInQml.cpp +++ b/src/ui/qml/UIPlugInQml.cpp @@ -12,15 +12,25 @@ #include "context/SelfAuthContext.h" #include "CardReturnCode.h" #include "ChangePinModel.h" -#include "DpiCalculator.h" #include "Env.h" #include "FileDestination.h" +#include "Initializer.h" +#include "LogHandler.h" #include "LogModel.h" +#include "NotificationModel.h" #include "PlatformTools.h" #include "ProviderCategoryFilterModel.h" +#include "ReaderScanEnabler.h" #include "RemoteServiceModel.h" +#include "SelfAuthModel.h" +#include "SelfDiagnosisModel.h" #include "Service.h" -#include "StatusBarUtil.h" +#include "SingletonHelper.h" +#include "SurveyModel.h" + +#if defined(Q_OS_WIN) || (defined(Q_OS_BSD4) && !defined(Q_OS_IOS)) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) +#include "ReaderDriverModel.h" +#endif #if defined(Q_OS_ANDROID) #include @@ -31,8 +41,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -44,6 +56,11 @@ Q_DECLARE_LOGGING_CATEGORY(qml) using namespace governikus; +static Initializer::Entry X([] { + qRegisterMetaType >("QList"); + }); + + template QObject* provideQmlType(QQmlEngine* pEngine, QJSEngine* pScriptEngine) { @@ -57,7 +74,7 @@ QObject* provideQmlType(QQmlEngine* pEngine, QJSEngine* pScriptEngine) template static QObject* provideSingletonQmlType(QQmlEngine* pEngine, QJSEngine* pScriptEngine) { - Q_UNUSED(pScriptEngine); + Q_UNUSED(pScriptEngine) const auto model = Env::getSingleton(); pEngine->setObjectOwnership(model, QQmlEngine::CppOwnership); @@ -66,7 +83,7 @@ static QObject* provideSingletonQmlType(QQmlEngine* pEngine, QJSEngine* pScriptE template -static void registerQmlType(QObject* (*pTypeProvider)(QQmlEngine*, QJSEngine*)) +static void registerQmlSingletonType(QObject* (*pTypeProvider)(QQmlEngine*, QJSEngine*)) { QByteArray qmlName(T::staticMetaObject.className()); qmlName.replace(QByteArrayLiteral("governikus::"), QByteArray()); @@ -77,29 +94,48 @@ static void registerQmlType(QObject* (*pTypeProvider)(QQmlEngine*, QJSEngine*)) } +template +static void registerQmlType() +{ + QByteArray qmlName(T::staticMetaObject.className()); + qmlName.replace(QByteArrayLiteral("governikus::"), QByteArray()); + + const QByteArray url = QByteArrayLiteral("Governikus.Type.") + qmlName; + + qmlRegisterType(url.constData(), 1, 0, qmlName.constData()); +} + + UIPlugInQml::UIPlugInQml() : mEngine() - , mHistoryModel(&Env::getSingleton()->getHistorySettings()) + , mQmlEngineWarningCount(0) , mVersionInformationModel() - , mQmlExtension() - , mSelfAuthModel() - , mSettingsModel() , mCertificateDescriptionModel() , mChatModel() , mExplicitPlatformStyle(getPlatformSelectors()) , mConnectivityManager() , mTrayIcon() +#if defined(Q_OS_MACOS) + , mMenuBar() +#endif { #if defined(Q_OS_ANDROID) QGuiApplication::setFont(QFont(QStringLiteral("Roboto"))); #endif +#ifdef Q_OS_WIN + QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering); +#endif + + QGuiApplication::setWindowIcon(mTrayIcon.getIcon()); + connect(&mTrayIcon, &TrayIcon::fireShow, this, &UIPlugInQml::show); connect(&mTrayIcon, &TrayIcon::fireQuit, this, &UIPlugInQml::fireQuitApplicationRequest); connect(Env::getSingleton(), &ChangePinModel::fireStartWorkflow, this, &UIPlugIn::fireChangePinRequest); - connect(&mSelfAuthModel, &SelfAuthModel::fireStartWorkflow, this, &UIPlugIn::fireSelfAuthenticationRequested); + connect(Env::getSingleton(), &SelfAuthModel::fireStartWorkflow, this, &UIPlugIn::fireSelfAuthenticationRequested); connect(Env::getSingleton(), &RemoteServiceModel::fireStartWorkflow, this, &UIPlugIn::fireRemoteServiceRequested); + connect(Env::getSingleton(), &LogHandler::fireRawLog, this, &UIPlugInQml::onRawLog, Qt::QueuedConnection); connect(this, &UIPlugIn::fireShowUserInformation, this, &UIPlugInQml::onShowUserInformation); init(); } @@ -110,17 +146,26 @@ void UIPlugInQml::registerQmlTypes() qmlRegisterUncreatableType("Governikus.Type.UiModule", 1, 0, "UiModule", QStringLiteral("Not creatable as it is an enum type")); qmlRegisterUncreatableType("Governikus.Type.ReaderPlugIn", 1, 0, "ReaderPlugIn", QStringLiteral("Not creatable as it is an enum type")); qmlRegisterUncreatableType("Governikus.Type.CardReturnCode", 1, 0, "CardReturnCode", QStringLiteral("Not creatable as it is an enum type")); - qmlRegisterUncreatableType("Governikus.Type.PacePasswordId", 1, 0, "PacePasswordId", QStringLiteral("Not creatable as it is an enum type")); - registerQmlType(&provideQmlType ); - registerQmlType(&provideQmlType ); + registerQmlType(); - registerQmlType(&provideSingletonQmlType ); - registerQmlType(&provideSingletonQmlType ); - registerQmlType(&provideSingletonQmlType ); - registerQmlType(&provideSingletonQmlType ); - registerQmlType(&provideSingletonQmlType ); - registerQmlType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideQmlType ); + registerQmlSingletonType(&provideQmlType ); +#if defined(Q_OS_WIN) || (defined(Q_OS_BSD4) && !defined(Q_OS_IOS)) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) + registerQmlSingletonType(&provideQmlType ); +#endif + + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); + registerQmlSingletonType(&provideSingletonQmlType ); } @@ -130,17 +175,17 @@ void UIPlugInQml::init() qputenv("QML_DISABLE_DISK_CACHE", "true"); #endif + logRenderingEnvironment(); + mEngine.reset(new QQmlApplicationEngine()); + connect(mEngine.data(), &QQmlApplicationEngine::warnings, this, &UIPlugInQml::onQmlWarnings, Qt::QueuedConnection); + connect(mEngine.data(), &QQmlApplicationEngine::objectCreated, this, &UIPlugInQml::onQmlObjectCreated, Qt::QueuedConnection); + mEngine->rootContext()->setContextProperty(QStringLiteral("plugin"), this); QQmlFileSelector::get(mEngine.data())->setExtraSelectors(mExplicitPlatformStyle.split(QLatin1Char(','))); - mEngine->rootContext()->setContextProperty(QStringLiteral("screenDpiScale"), DpiCalculator::getDpiScale()); - mEngine->rootContext()->setContextProperty(QStringLiteral("historyModel"), &mHistoryModel); mEngine->rootContext()->setContextProperty(QStringLiteral("versionInformationModel"), &mVersionInformationModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("qmlExtension"), &mQmlExtension); - mEngine->rootContext()->setContextProperty(QStringLiteral("selfAuthModel"), &mSelfAuthModel); - mEngine->rootContext()->setContextProperty(QStringLiteral("settingsModel"), &mSettingsModel); mEngine->rootContext()->setContextProperty(QStringLiteral("certificateDescriptionModel"), &mCertificateDescriptionModel); mEngine->rootContext()->setContextProperty(QStringLiteral("chatModel"), &mChatModel); mEngine->rootContext()->setContextProperty(QStringLiteral("connectivityManager"), &mConnectivityManager); @@ -161,10 +206,40 @@ void UIPlugInQml::hide() } +void UIPlugInQml::switchUi() +{ + auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + generalSettings.setSelectedUi(QStringLiteral("widgets")); + generalSettings.save(); + + Q_EMIT fireRestartApplicationRequested(); +} + + +void UIPlugInQml::logRenderingEnvironment() const +{ + QString openGLContext = QStringLiteral("Unknown"); + switch (QOpenGLContext::openGLModuleType()) + { + case QOpenGLContext::LibGL: + openGLContext = QStringLiteral("OpenGL"); + break; + + case QOpenGLContext::LibGLES: + openGLContext = QStringLiteral("OpenGL ES 2.0 or higher"); + break; + } + qCDebug(qml).noquote() << "QOpenGLContext:" << openGLContext; + + // Activate logging of Qt scenegraph information on startup, e.g. GL_RENDERER, GL_VERSION, ... + qputenv("QSG_INFO", "1"); +} + + QString UIPlugInQml::getPlatformSelectors() const { #ifndef QT_NO_DEBUG - const char* overrideSelector = "OVERRIDE_PLATFORM_SELECTOR"; + const char* const overrideSelector = "OVERRIDE_PLATFORM_SELECTOR"; if (!qEnvironmentVariableIsEmpty(overrideSelector)) { const auto& platform = QString::fromLocal8Bit(qgetenv(overrideSelector)); @@ -177,35 +252,12 @@ QString UIPlugInQml::getPlatformSelectors() const } #endif -#if defined(Q_OS_ANDROID) - const jboolean result = QtAndroid::androidActivity().callMethod("isTablet", "()Z"); - const bool isTablet = result != JNI_FALSE; -#else - // A device with a screen diagnonal above 6 inches is considered a tablet. - static const double MAX_SMARTPHONE_DIAGONAL_IN = 6.0; - static const double MAX_SMARTPHONE_DIAGONAL_MM = 25.4 * MAX_SMARTPHONE_DIAGONAL_IN; - - const QList screens = QGuiApplication::screens(); - - Q_ASSERT(!screens.isEmpty()); - - QScreen* const mainScreen = screens.first(); - Q_ASSERT(mainScreen != nullptr); - - const QSizeF size = mainScreen->physicalSize(); - const double width = size.width(); - const double height = size.height(); - const double diagonal = sqrt(width * width + height * height); - const bool isTablet = diagonal > MAX_SMARTPHONE_DIAGONAL_MM; -#endif - #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) const QString platform = QStringLiteral("mobile,"); - const QString tablet = (isTablet ? QStringLiteral("tablet,") : QStringLiteral("phone,")); + const QString tablet = (isTablet() ? QStringLiteral("tablet,") : QStringLiteral("phone,")); const QString brand = QGuiApplication::platformName(); #else const QString platform = QStringLiteral("desktop,"); - Q_UNUSED(isTablet); const QString tablet; #if defined(Q_OS_MAC) const QString brand = QStringLiteral("mac"); @@ -220,19 +272,19 @@ QString UIPlugInQml::getPlatformSelectors() const void UIPlugInQml::onWorkflowStarted(QSharedPointer pContext) { - onShowUi(UiModule::IDENTIFY); - mQmlExtension.keepScreenOn(true); - + Env::getSingleton()->keepScreenOn(true); Env::getSingleton()->resetContext(pContext); Env::getSingleton()->resetContext(pContext); if (auto changePinContext = pContext.objectCast()) { + onShowUi(UiModule::PINMANAGEMENT); Env::getSingleton()->resetContext(changePinContext); } if (auto authContext = pContext.objectCast()) { + onShowUi(UiModule::IDENTIFY); mConnectivityManager.startWatching(); Env::getSingleton()->resetContext(authContext); mCertificateDescriptionModel.resetContext(authContext); @@ -241,7 +293,8 @@ void UIPlugInQml::onWorkflowStarted(QSharedPointer pContext) if (auto authContext = pContext.objectCast()) { - mSelfAuthModel.resetContext(authContext); + onShowUi(UiModule::IDENTIFY); + Env::getSingleton()->resetContext(authContext); } if (auto remoteServiceContext = pContext.objectCast()) @@ -253,8 +306,7 @@ void UIPlugInQml::onWorkflowStarted(QSharedPointer pContext) void UIPlugInQml::onWorkflowFinished(QSharedPointer pContext) { - mQmlExtension.keepScreenOn(false); - + Env::getSingleton()->keepScreenOn(false); Env::getSingleton()->resetContext(); Env::getSingleton()->resetContext(); @@ -269,11 +321,17 @@ void UIPlugInQml::onWorkflowFinished(QSharedPointer pContext) Env::getSingleton()->resetContext(); mCertificateDescriptionModel.resetContext(); mChatModel.resetContext(); + + const auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + if (!pContext.objectCast() && generalSettings.isAutoCloseWindowAfterAuthentication()) + { + Q_EMIT fireHideRequest(); + } } if (pContext.objectCast()) { - mSelfAuthModel.resetContext(); + Env::getSingleton()->resetContext(); } if (pContext.objectCast()) @@ -286,7 +344,17 @@ void UIPlugInQml::onWorkflowFinished(QSharedPointer pContext) void UIPlugInQml::onApplicationStarted() { mTrayIcon.create(); - show(); + +#if defined(Q_OS_WIN) || (defined(Q_OS_BSD4) && !defined(Q_OS_IOS)) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) + if (!QSystemTrayIcon::isSystemTrayAvailable() + || Env::getSingleton()->getGeneralSettings().isShowSetupAssistant() + || Env::getSingleton()->getGeneralSettings().isDeveloperMode()) +#endif + { + QMetaObject::invokeMethod(this, &UIPlugInQml::show, Qt::QueuedConnection); + } + + Q_EMIT fireSafeAreaMarginsChanged(); } @@ -297,15 +365,47 @@ void UIPlugInQml::onShowUi(UiModule pModule) } +void UIPlugInQml::onHideUi() +{ + Q_EMIT fireHideRequest(); +} + + +void UIPlugInQml::onUiDomination(const UIPlugIn* pUi, const QString& pInformation, bool pAccepted) +{ + if (pUi == this) + { + return; + } + + if (pAccepted) + { + mDominator = pInformation.isEmpty() ? QLatin1String("") : pInformation; + Q_EMIT fireDominatorChanged(); + } +} + + +void UIPlugInQml::onUiDominationReleased() +{ + if (!mDominator.isNull()) + { + mDominator.clear(); + Q_EMIT fireDominatorChanged(); + } +} + + void UIPlugInQml::onShowUserInformation(const QString& pMessage) { - mQmlExtension.showFeedback(pMessage); + Env::getSingleton()->showFeedback(pMessage); } void UIPlugInQml::show() { onShowUi(UiModule::CURRENT); + Env::getSingleton()->runUpdateIfNeeded(); } @@ -335,6 +435,46 @@ QUrl UIPlugInQml::getPath(const QString& pRelativePath, bool pQrc) } +#ifndef Q_OS_IOS +bool UIPlugInQml::isTablet() const +{ +#ifdef Q_OS_ANDROID + const jboolean result = QtAndroid::androidActivity().callMethod("isTablet", "()Z"); + return result != JNI_FALSE; + +#else + return false; + +#endif +} + + +#endif + + +void UIPlugInQml::onQmlWarnings(const QList& pWarnings) +{ + mQmlEngineWarningCount += pWarnings.size(); +} + + +void UIPlugInQml::onQmlObjectCreated(QObject* pObject) +{ + const bool fatalErrors = pObject == nullptr; + const QString result = fatalErrors ? QStringLiteral("fatal errors.") : QStringLiteral("%1 warnings.").arg(mQmlEngineWarningCount); + qCDebug(qml).noquote() << "QML engine initialization finished with" << result; +} + + +void UIPlugInQml::onRawLog(const QString& pMessage, const QString& pCategoryName) +{ + if (pCategoryName == QLatin1String("developermode") || pCategoryName == QLatin1String("feedback")) + { + mTrayIcon.showMessage(QCoreApplication::applicationName(), pMessage); + } +} + + void UIPlugInQml::doRefresh() { qCDebug(qml) << "Reload qml files"; @@ -342,28 +482,12 @@ void UIPlugInQml::doRefresh() } -bool UIPlugInQml::useFlatStyleOnDesktop() const -{ - return QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows8); -} - - QString UIPlugInQml::getPlatformStyle() const { return mExplicitPlatformStyle; } -void UIPlugInQml::applyPlatformStyle(const QString& pPlatformStyle) -{ - if (mExplicitPlatformStyle != pPlatformStyle) - { - mExplicitPlatformStyle = pPlatformStyle; - doRefresh(); - } -} - - bool UIPlugInQml::isDeveloperBuild() const { #ifndef QT_NO_DEBUG @@ -374,3 +498,50 @@ bool UIPlugInQml::isDeveloperBuild() const #endif } + + +QString UIPlugInQml::getDominator() const +{ + return mDominator; +} + + +bool UIPlugInQml::isDominated() const +{ + return !mDominator.isNull(); +} + + +#ifndef Q_OS_IOS +QVariantMap UIPlugInQml::getSafeAreaMargins() const +{ + QVariantMap marginMap; + +#ifdef Q_OS_ANDROID + auto screen = QGuiApplication::primaryScreen(); + marginMap[QStringLiteral("top")] = QtAndroid::androidActivity().callMethod("getStatusBarHeight", "()I") / screen->devicePixelRatio(); + marginMap[QStringLiteral("right")] = 0; + marginMap[QStringLiteral("bottom")] = 0; + marginMap[QStringLiteral("left")] = 0; +#else + marginMap[QStringLiteral("top")] = 0; + marginMap[QStringLiteral("right")] = 0; + marginMap[QStringLiteral("bottom")] = 0; + marginMap[QStringLiteral("left")] = 0; +#endif + + return marginMap; +} + + +#endif + + +void UIPlugInQml::applyPlatformStyle(const QString& pPlatformStyle) +{ + if (mExplicitPlatformStyle != pPlatformStyle) + { + mExplicitPlatformStyle = pPlatformStyle; + doRefresh(); + } +} diff --git a/src/ui/qml/UIPlugInQml.h b/src/ui/qml/UIPlugInQml.h index 637fa63..42ac3ed 100644 --- a/src/ui/qml/UIPlugInQml.h +++ b/src/ui/qml/UIPlugInQml.h @@ -11,8 +11,6 @@ #include "ConnectivityManager.h" #include "HistoryModel.h" #include "NumberModel.h" -#include "QmlExtension.h" -#include "SelfAuthModel.h" #include "SettingsModel.h" #include "TrayIcon.h" #include "UIPlugIn.h" @@ -20,6 +18,9 @@ #include #include +#if defined (Q_OS_MACOS) +#include +#endif namespace governikus { @@ -32,23 +33,28 @@ class UIPlugInQml Q_INTERFACES(governikus::UIPlugIn) Q_PROPERTY(QString platformStyle READ getPlatformStyle CONSTANT) Q_PROPERTY(bool developerBuild READ isDeveloperBuild CONSTANT) + Q_PROPERTY(QString dominator READ getDominator NOTIFY fireDominatorChanged) + Q_PROPERTY(bool dominated READ isDominated NOTIFY fireDominatorChanged) + Q_PROPERTY(QVariantMap safeAreaMargins READ getSafeAreaMargins NOTIFY fireSafeAreaMarginsChanged) private: QScopedPointer mEngine; - HistoryModel mHistoryModel; + int mQmlEngineWarningCount; VersionInformationModel mVersionInformationModel; - QmlExtension mQmlExtension; - SelfAuthModel mSelfAuthModel; - SettingsModel mSettingsModel; CertificateDescriptionModel mCertificateDescriptionModel; ChatModel mChatModel; QString mExplicitPlatformStyle; ConnectivityManager mConnectivityManager; TrayIcon mTrayIcon; + QString mDominator; +#if defined(Q_OS_MACOS) + QMenuBar mMenuBar; +#endif + void logRenderingEnvironment() const; QString getPlatformSelectors() const; static QUrl getPath(const QString& pRelativePath, bool pQrc = true); - void createTrayIcon(); + bool isTablet() const; public: UIPlugInQml(); @@ -56,15 +62,22 @@ class UIPlugInQml static void registerQmlTypes(); - Q_INVOKABLE bool useFlatStyleOnDesktop() const; QString getPlatformStyle() const; + bool isDeveloperBuild() const; + QString getDominator() const; + bool isDominated() const; + QVariantMap getSafeAreaMargins() const; + Q_INVOKABLE void applyPlatformStyle(const QString& pPlatformStyle); - Q_INVOKABLE bool isDeveloperBuild() const; Q_INVOKABLE void init(); Q_INVOKABLE void hide(); + Q_INVOKABLE void switchUi(); Q_SIGNALS: void fireShowRequest(UiModule pModule); + void fireHideRequest(); + void fireDominatorChanged(); + void fireSafeAreaMarginsChanged(); private Q_SLOTS: void show(); @@ -73,8 +86,16 @@ class UIPlugInQml virtual void onWorkflowFinished(QSharedPointer pContext) override; virtual void onApplicationStarted() override; virtual void onShowUi(UiModule pModule) override; + virtual void onHideUi() override; + virtual void onUiDomination(const UIPlugIn* pUi, const QString& pInformation, bool pAccepted) override; + virtual void onUiDominationReleased() override; void onShowUserInformation(const QString& pMessage); + void onQmlWarnings(const QList& pWarnings); + void onQmlObjectCreated(QObject* pObject); + + void onRawLog(const QString& pMessage, const QString& pCategoryName); + public Q_SLOTS: void doRefresh(); }; diff --git a/src/ui/qml/UIPlugInQml_ios.mm b/src/ui/qml/UIPlugInQml_ios.mm new file mode 100644 index 0000000..3a10622 --- /dev/null +++ b/src/ui/qml/UIPlugInQml_ios.mm @@ -0,0 +1,32 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "UIPlugInQml.h" + +#import + +using namespace governikus; + + +bool UIPlugInQml::isTablet() const +{ + return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad; +} + + +QVariantMap UIPlugInQml::getSafeAreaMargins() const +{ + UIViewController* rootController = [[UIApplication sharedApplication].windows[0] rootViewController]; + + UIEdgeInsets safeAreaInsets = [rootController view].safeAreaInsets; + + QVariantMap insetMap; + + insetMap[QStringLiteral("top")] = safeAreaInsets.top; + insetMap[QStringLiteral("right")] = safeAreaInsets.right; + insetMap[QStringLiteral("bottom")] = safeAreaInsets.bottom; + insetMap[QStringLiteral("left")] = safeAreaInsets.left; + + return insetMap; +} diff --git a/src/ui/qml/VersionInformationModel.cpp b/src/ui/qml/VersionInformationModel.cpp index 686f29b..a4b3964 100644 --- a/src/ui/qml/VersionInformationModel.cpp +++ b/src/ui/qml/VersionInformationModel.cpp @@ -56,7 +56,7 @@ QVariant VersionInformationModel::data(const QModelIndex& pIndex, int pRole) con { if (pIndex.isValid() && pIndex.row() < rowCount()) { - auto entry = mData[pIndex.row()]; + const auto& entry = qAsConst(mData).at(pIndex.row()); if (pRole == LABEL) { return entry.first; diff --git a/src/ui/qml/WorkflowModel.cpp b/src/ui/qml/WorkflowModel.cpp index da6ad54..63522bc 100644 --- a/src/ui/qml/WorkflowModel.cpp +++ b/src/ui/qml/WorkflowModel.cpp @@ -5,11 +5,14 @@ #include "WorkflowModel.h" #include "AppSettings.h" +#include "Email.h" #include "FuncUtils.h" #include "GeneralSettings.h" #include "ReaderConfiguration.h" #include "ReaderManager.h" +#include + using namespace governikus; @@ -161,8 +164,15 @@ bool WorkflowModel::getNextWorkflowPending() const } +QString WorkflowModel::getReaderImage() const +{ + return mReaderImage; +} + + void WorkflowModel::setInitialPluginType() { +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) const GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); const QString& lastReaderPluginTypeString = settings.getLastReaderPluginType(); @@ -170,16 +180,17 @@ void WorkflowModel::setInitialPluginType() if (lastReaderPluginType == ReaderManagerPlugInType::UNKNOWN) { -#if defined(Q_OS_ANDROID) setReaderPlugInType(ReaderManagerPlugInType::NFC); -#elif defined(Q_OS_IOS) - setReaderPlugInType(ReaderManagerPlugInType::BLUETOOTH); -#else - setReaderPlugInType(ReaderManagerPlugInType::PCSC); -#endif return; } setReaderPlugInType(lastReaderPluginType); +#else + if (!mContext) + { + return; + } + mContext->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC, ReaderManagerPlugInType::REMOTE}); +#endif } @@ -193,6 +204,41 @@ bool WorkflowModel::selectedReaderHasCard() const } +bool WorkflowModel::shouldSkipResultView() const +{ + if (!mContext) + { + return false; + } + // We deliberately don't want to use GlobalStatus::isCancellationByUser(), because that would also skip the + // ResultView when the user pressed Cancel on his card reader. + return mContext->getStatus().getStatusCode() == GlobalStatus::Code::Workflow_Cancellation_By_User; +} + + +bool WorkflowModel::isCancellationByUser() const +{ + if (!mContext) + { + return false; + } + return mContext->getStatus().isCancellationByUser(); +} + + +void WorkflowModel::sendResultMail() const +{ + Q_ASSERT(mContext); + const GlobalStatus status = mContext->getStatus(); + //: Subject from error report mail + QString mailSubject = tr("AusweisApp2 error report - %1").arg(status.toErrorDescription()); + QString mailBody = generateMailBody(status); + QString url = QStringLiteral("mailto:support@ausweisapp.de?subject=%1&body=%2").arg(mailSubject, mailBody); + + QDesktopServices::openUrl(url); +} + + void WorkflowModel::onReaderManagerSignal() { QString newReaderImage; @@ -200,11 +246,11 @@ void WorkflowModel::onReaderManagerSignal() const auto& readersWithNPA = filter([](const ReaderInfo& i){return i.hasEidCard();}, readerInfos); if (readersWithNPA.size() == 1) { - newReaderImage = readersWithNPA.at(0).getReaderConfigurationInfo().getIconWithNPA()->lookupPath(); + newReaderImage = readersWithNPA.at(0).getReaderConfigurationInfo().getIconWithNPA()->lookupUrl().toString(); } else if (readerInfos.size() == 1) { - newReaderImage = readerInfos.at(0).getReaderConfigurationInfo().getIcon()->lookupPath(); + newReaderImage = readerInfos.at(0).getReaderConfigurationInfo().getIcon()->lookupUrl().toString(); } else if (readerInfos.size() > 1) { diff --git a/src/ui/qml/WorkflowModel.h b/src/ui/qml/WorkflowModel.h index 368f376..8670824 100644 --- a/src/ui/qml/WorkflowModel.h +++ b/src/ui/qml/WorkflowModel.h @@ -28,7 +28,7 @@ class WorkflowModel Q_PROPERTY(bool errorIsMasked READ isMaskedError NOTIFY fireResultChanged) Q_PROPERTY(ReaderManagerPlugInType readerPlugInType READ getReaderPlugInType WRITE setReaderPlugInType NOTIFY fireReaderPlugInTypeChanged) Q_PROPERTY(bool isBasicReader READ isBasicReader NOTIFY fireIsBasicReaderChanged) - Q_PROPERTY(QString readerImage MEMBER mReaderImage NOTIFY fireReaderImageChanged) + Q_PROPERTY(QString readerImage READ getReaderImage NOTIFY fireReaderImageChanged) Q_PROPERTY(bool hasNextWorkflowPending READ getNextWorkflowPending NOTIFY fireNextWorkflowPendingChanged) private: @@ -55,6 +55,8 @@ class WorkflowModel bool getNextWorkflowPending() const; + QString getReaderImage() const; + Q_INVOKABLE void startWorkflow(); Q_INVOKABLE void cancelWorkflow(); Q_INVOKABLE void cancelWorkflowOnPinBlocked(); @@ -62,6 +64,9 @@ class WorkflowModel Q_INVOKABLE void continueWorkflow(); Q_INVOKABLE void setInitialPluginType(); Q_INVOKABLE bool selectedReaderHasCard() const; + Q_INVOKABLE bool shouldSkipResultView() const; + Q_INVOKABLE bool isCancellationByUser() const; + Q_INVOKABLE void sendResultMail() const; public Q_SLOTS: void onReaderManagerSignal(); diff --git a/src/ui/websocket/CMakeLists.txt b/src/ui/websocket/CMakeLists.txt index 2d39144..9797813 100644 --- a/src/ui/websocket/CMakeLists.txt +++ b/src/ui/websocket/CMakeLists.txt @@ -8,6 +8,6 @@ IF(TARGET Qt5::WebSockets) ADD_PLATFORM_LIBRARY(AusweisAppUiWebsocket) - TARGET_LINK_LIBRARIES(AusweisAppUiWebsocket Qt5::Core Qt5::WebSockets AusweisAppUi AusweisAppUiJsonApi AusweisAppNetwork AusweisAppGlobal) + TARGET_LINK_LIBRARIES(AusweisAppUiWebsocket Qt5::Core Qt5::WebSockets AusweisAppUi AusweisAppUiJson AusweisAppNetwork AusweisAppGlobal) TARGET_COMPILE_DEFINITIONS(AusweisAppUiWebsocket PRIVATE QT_STATICPLUGIN) ENDIF() diff --git a/src/ui/websocket/UIPlugInWebSocket.cpp b/src/ui/websocket/UIPlugInWebSocket.cpp index 14282c5..8fee203 100644 --- a/src/ui/websocket/UIPlugInWebSocket.cpp +++ b/src/ui/websocket/UIPlugInWebSocket.cpp @@ -27,18 +27,18 @@ UIPlugInWebSocket::UIPlugInWebSocket() , mServer(QCoreApplication::applicationName() + QLatin1Char('/') + QCoreApplication::applicationVersion(), QWebSocketServer::NonSecureMode) , mConnection(nullptr) , mRequest() - , mJsonApi(nullptr) + , mJson(nullptr) , mContext() , mUiDomination(false) { - if (!UILoader::getInstance().load(UIPlugInName::UIPlugInJsonApi)) + if (!UILoader::getInstance().load(UIPlugInName::UIPlugInJson)) { qCWarning(websocket) << "Cannot start WebSocket because JSON-API is missing"; return; } - mJsonApi = qobject_cast(UILoader::getInstance().getLoaded(UIPlugInName::UIPlugInJsonApi)); - Q_ASSERT(mJsonApi); + mJson = qobject_cast(UILoader::getInstance().getLoaded(UIPlugInName::UIPlugInJson)); + Q_ASSERT(mJson); mHttpServer = Env::getShared(); if (mHttpServer->isListening()) @@ -67,7 +67,7 @@ void UIPlugInWebSocket::onWorkflowStarted(QSharedPointer pConte void UIPlugInWebSocket::onWorkflowFinished(QSharedPointer pContext) { - Q_UNUSED(pContext); + Q_UNUSED(pContext) mContext.clear(); } @@ -101,7 +101,7 @@ void UIPlugInWebSocket::onUiDomination(const UIPlugIn* pUi, const QString& pInfo void UIPlugInWebSocket::onUiDominationReleased() { mUiDomination = false; - mJsonApi->setEnabled(false); + mJson->setEnabled(false); Env::getSingleton()->stopScanAll(); Env::getSingleton()->setUsedAsSDK(false); } @@ -136,10 +136,10 @@ void UIPlugInWebSocket::onNewConnection() if (mServer.hasPendingConnections()) { mConnection.reset(mServer.nextPendingConnection()); - connect(mJsonApi, &UIPlugInJsonApi::fireMessage, this, &UIPlugInWebSocket::onJsonApiMessage); + connect(mJson, &UIPlugInJson::fireMessage, this, &UIPlugInWebSocket::onJsonMessage); connect(mConnection.data(), &QWebSocket::textMessageReceived, this, &UIPlugInWebSocket::onTextMessageReceived); connect(mConnection.data(), &QWebSocket::disconnected, this, &UIPlugInWebSocket::onClientDisconnected); - mJsonApi->setEnabled(); + mJson->setEnabled(); } else { @@ -156,13 +156,13 @@ void UIPlugInWebSocket::onClientDisconnected() if (mContext && mUiDomination) { - const QSignalBlocker blocker(mJsonApi); + const QSignalBlocker blocker(mJson); Q_EMIT mContext->fireCancelWorkflow(); } mConnection.reset(); mRequest.reset(); - disconnect(mJsonApi, &UIPlugInJsonApi::fireMessage, this, &UIPlugInWebSocket::onJsonApiMessage); + disconnect(mJson, &UIPlugInJson::fireMessage, this, &UIPlugInWebSocket::onJsonMessage); Q_EMIT fireUiDominationRelease(); } @@ -171,12 +171,12 @@ void UIPlugInWebSocket::onTextMessageReceived(const QString& pMessage) { if (mConnection) { - mJsonApi->doMessageProcessing(pMessage.toUtf8()); + mJson->doMessageProcessing(pMessage.toUtf8()); } } -void UIPlugInWebSocket::onJsonApiMessage(const QByteArray& pMessage) +void UIPlugInWebSocket::onJsonMessage(const QByteArray& pMessage) { if (mConnection) { diff --git a/src/ui/websocket/UIPlugInWebSocket.h b/src/ui/websocket/UIPlugInWebSocket.h index d483f7a..bd247e1 100644 --- a/src/ui/websocket/UIPlugInWebSocket.h +++ b/src/ui/websocket/UIPlugInWebSocket.h @@ -9,7 +9,7 @@ #include "HttpRequest.h" #include "HttpServer.h" #include "UIPlugIn.h" -#include "UIPlugInJsonApi.h" +#include "UIPlugInJson.h" #include #include @@ -32,7 +32,7 @@ class UIPlugInWebSocket QWebSocketServer mServer; QScopedPointer mConnection; QSharedPointer mRequest; - UIPlugInJsonApi* mJsonApi; + UIPlugInJson* mJson; QSharedPointer mContext; bool mUiDomination; @@ -46,7 +46,7 @@ class UIPlugInWebSocket void onNewConnection(); void onClientDisconnected(); void onTextMessageReceived(const QString& pMessage); - void onJsonApiMessage(const QByteArray& pMessage); + void onJsonMessage(const QByteArray& pMessage); public: UIPlugInWebSocket(); diff --git a/src/ui/widget/AboutDialog.cpp b/src/ui/widget/AboutDialog.cpp index ef47707..781ff00 100644 --- a/src/ui/widget/AboutDialog.cpp +++ b/src/ui/widget/AboutDialog.cpp @@ -23,8 +23,8 @@ AboutDialog::AboutDialog(QWidget* pParent) layout()->setSizeConstraint(QLayout::SetFixedSize); // For platform != Windows: Disable maximize button setWindowModality(Qt::WindowModal); // For platform == macOS: Make dialog slide in from the top - const SecureStorage& storage = SecureStorage::getInstance(); - const auto& url = VersionNumber::getApplicationVersion().isDeveloperVersion() ? storage.getAppcastBetaUpdateUrl() : storage.getAppcastUpdateUrl(); + const auto* storage = Env::getSingleton(); + const auto& url = VersionNumber::getApplicationVersion().isDeveloperVersion() ? storage->getAppcastBetaUpdateUrl() : storage->getAppcastUpdateUrl(); const auto& releaseNotes = url.adjusted(QUrl::RemoveFilename).toString() + QStringLiteral("ReleaseNotes.html"); setWindowTitle(tr("About %1 - %2").arg(QCoreApplication::applicationName(), QCoreApplication::organizationName())); diff --git a/src/ui/widget/AboutDialog.ui b/src/ui/widget/AboutDialog.ui index 2c3f7d4..db14727 100644 --- a/src/ui/widget/AboutDialog.ui +++ b/src/ui/widget/AboutDialog.ui @@ -106,7 +106,7 @@ - AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Ministry of the Interior, Building and Community. + AusweisApp2 is a product of Governikus GmbH & Co. KG - on behalf of the Federal Office for Information Security. Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop diff --git a/src/ui/widget/AppQtGui.cpp b/src/ui/widget/AppQtGui.cpp index 77bb636..36d6edf 100644 --- a/src/ui/widget/AppQtGui.cpp +++ b/src/ui/widget/AppQtGui.cpp @@ -38,7 +38,6 @@ Q_DECLARE_LOGGING_CATEGORY(gui) AppQtGui::AppQtGui() : QObject() , mMainWidget(new AppQtMainWidget()) - , mIcon(QStringLiteral(":/images/npa.svg")) , mTrayIcon() , mActiveWorkflowUi() , mSetupAssistantGui(nullptr) @@ -46,13 +45,15 @@ AppQtGui::AppQtGui() , mUpdateInfo(new QMessageBox(mMainWidget)) , mCertificateInfo(new QMessageBox(mMainWidget)) , mLockedInfo(new QMessageBox(mMainWidget)) + , mSwitchUiInquiry(new QMessageBox(mMainWidget)) , mUpdateWindow(new UpdateWindow(mMainWidget)) , mAggressiveToForeground(false) { loadStyleSheet(); - mMainWidget->setWindowIcon(mIcon); + mMainWidget->setWindowIcon(mTrayIcon.getIcon()); connect(mMainWidget, &AppQtMainWidget::fireCloseActiveDialogs, this, &AppQtGui::onCloseActiveDialogs); + connect(mMainWidget, &AppQtMainWidget::fireSwitchUiRequested, this, &AppQtGui::onSwitchUiRequested); connect(&mTrayIcon, &TrayIcon::fireShow, this, [this] { AppQtGui::show(); @@ -60,7 +61,7 @@ AppQtGui::AppQtGui() connect(&mTrayIcon, &TrayIcon::fireQuit, this, &AppQtGui::quitApplicationRequested); mUpdateInfo->setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tr("Updates")); - mUpdateInfo->setWindowIcon(mIcon); + mUpdateInfo->setWindowIcon(mTrayIcon.getIcon()); mUpdateInfo->setWindowModality(Qt::WindowModal); mUpdateInfo->setStandardButtons(QMessageBox::Ok); mUpdateInfo->button(QMessageBox::Ok)->setFocus(); @@ -81,13 +82,19 @@ AppQtGui::AppQtGui() mLockedInfo->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("SDK")); mLockedInfo->setWindowFlags(mLockedInfo->windowFlags() & ~Qt::WindowCloseButtonHint& ~Qt::WindowContextHelpButtonHint& ~Qt::WindowMinMaxButtonsHint); - mLockedInfo->setWindowIcon(mIcon); + mLockedInfo->setWindowIcon(mTrayIcon.getIcon()); mLockedInfo->setWindowModality(Qt::WindowModal); mLockedInfo->setIcon(QMessageBox::Information); mLockedInfo->setText(tr("Another application uses AusweisApp2.")); mLockedInfo->setStandardButtons(QMessageBox::NoButton); - Service* service = Env::getSingleton(); + mSwitchUiInquiry->setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + tr("Switch UI")); + mSwitchUiInquiry->setIcon(QMessageBox::Information); + mSwitchUiInquiry->setText(tr("Do you want to switch to the new beta UI? You can switch back to the old UI in \"Settings\".")); + mSwitchUiInquiry->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + mSwitchUiInquiry->button(QMessageBox::Yes)->setFocus(); + + auto* service = Env::getSingleton(); connect(service, &Service::fireAppUpdateFinished, this, &AppQtGui::onAppUpdateReady); connect(service, &Service::fireUpdateScheduled, this, &AppQtGui::onUpdateScheduled); } @@ -222,7 +229,7 @@ void AppQtGui::onSetupAssistantWizardRequest() { Env::getSingleton()->runUpdateIfNeeded(); - if (!mSetupAssistantGui) + if (mSetupAssistantGui == nullptr) { mSetupAssistantGui = new SetupAssistantGui(mMainWidget); connect(mSetupAssistantGui, &SetupAssistantGui::fireChangePinButtonClicked, mMainWidget, &AppQtMainWidget::onChangePinButtonClicked); @@ -264,7 +271,7 @@ void AppQtGui::onDeveloperModeQuestion() void AppQtGui::onDiagnosisRequested() { - if (!mDiagnosisGui) + if (mDiagnosisGui == nullptr) { mDiagnosisGui = new DiagnosisGui(mMainWidget); } @@ -309,7 +316,7 @@ bool AppQtGui::eventFilter(QObject* /*pObject*/, QEvent* pEvent) { if (pEvent->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(pEvent); + auto* keyEvent = static_cast(pEvent); if (keyEvent->key() == Qt::Key_F1) { HelpAction::openContextHelp(); @@ -365,6 +372,10 @@ void AppQtGui::closeDialogs() { mDiagnosisGui->deactivate(); } + if (mSwitchUiInquiry) + { + mSwitchUiInquiry->reject(); + } } @@ -410,6 +421,21 @@ void AppQtGui::onCloseWindowRequested(bool* pDoClose) } +void AppQtGui::onSwitchUiRequested() +{ + if (mSwitchUiInquiry->exec() != QMessageBox::Yes) + { + return; + } + + auto& generalSettings = Env::getSingleton()->getGeneralSettings(); + generalSettings.setSelectedUi(QStringLiteral("qml")); + generalSettings.save(); + + Q_EMIT fireRestartApplicationRequested(); +} + + void AppQtGui::onCloseActiveDialogs() { if (mSetupAssistantGui != nullptr) @@ -571,7 +597,7 @@ void AppQtGui::onUpdateScheduled() } -void AppQtGui::onCertificateRemoved(QString pDeviceName) +void AppQtGui::onCertificateRemoved(const QString& pDeviceName) { mCertificateInfo->setText(tr("The device \"%1\" was unpaired because it does not react to connection attempts. Retry the pairing process if you want to use this device to authenticate yourself.").arg(pDeviceName)); mCertificateInfo->show(); diff --git a/src/ui/widget/AppQtGui.h b/src/ui/widget/AppQtGui.h index 7426057..a32f5bf 100644 --- a/src/ui/widget/AppQtGui.h +++ b/src/ui/widget/AppQtGui.h @@ -69,11 +69,11 @@ class AppQtGui void onDiagnosisRequested(); void onAppUpdateReady(bool pSuccess, const GlobalStatus& pError); void onUpdateScheduled(); - void onCertificateRemoved(QString pDeviceName); + void onCertificateRemoved(const QString& pDeviceName); + void onSwitchUiRequested(); private: AppQtMainWidget* mMainWidget; - QIcon mIcon; TrayIcon mTrayIcon; QSharedPointer mActiveWorkflowUi; SetupAssistantGui* mSetupAssistantGui; @@ -81,6 +81,7 @@ class AppQtGui QMessageBox* mUpdateInfo; QMessageBox* mCertificateInfo; QMessageBox* mLockedInfo; + QMessageBox* mSwitchUiInquiry; UpdateWindow* mUpdateWindow; bool mAggressiveToForeground; @@ -90,6 +91,7 @@ class AppQtGui void fireChangePinRequested(); void selfAuthenticationRequested(); void quitApplicationRequested(); + void fireRestartApplicationRequested(); void fireCloseActiveDialogs(); }; diff --git a/src/ui/widget/AppQtMainWidget.cpp b/src/ui/widget/AppQtMainWidget.cpp index b1cf089..130ad66 100644 --- a/src/ui/widget/AppQtMainWidget.cpp +++ b/src/ui/widget/AppQtMainWidget.cpp @@ -55,7 +55,7 @@ AppQtMainWidget::AppQtMainWidget() mUi->appLogoWidget->setAttribute(Qt::WA_TransparentForMouseEvents); - QStackedLayout* centralStackLayout = qobject_cast(mUi->centralWidget->layout()); + auto* centralStackLayout = qobject_cast(mUi->centralWidget->layout()); centralStackLayout->setStackingMode(QStackedLayout::StackAll); centralStackLayout->setCurrentIndex(1); @@ -92,6 +92,7 @@ AppQtMainWidget::AppQtMainWidget() connect(mUi->settingsPage, &SettingsWidget::changePinRequested, this, &AppQtMainWidget::fireChangePinRequested); connect(mUi->settingsPage, &SettingsWidget::diagnosisRequested, this, &AppQtMainWidget::fireDiagnosisRequested); connect(mUi->settingsPage, &SettingsWidget::settingsDone, this, &AppQtMainWidget::onSettingsDone); + connect(mUi->settingsPage, &SettingsWidget::fireSwitchUiRequested, this, &AppQtMainWidget::fireSwitchUiRequested); connect(mUi->germanButton, &QPushButton::clicked, this, [&]() { @@ -168,7 +169,7 @@ AppQtMainWidget::~AppQtMainWidget() void AppQtMainWidget::showEvent(QShowEvent* pEvent) { - Q_UNUSED(pEvent); + Q_UNUSED(pEvent) mCloseWithoutConfirmation = false; } @@ -251,7 +252,7 @@ void AppQtMainWidget::workflowActivated(WorkflowWidgetParent pParent, const QStr while (containingWidget != nullptr) { QWidget* containerParent = containingWidget->parentWidget(); - if (QStackedWidget* stackedWidget = qobject_cast(containerParent)) + if (auto* stackedWidget = qobject_cast(containerParent)) { mSelectedPagesBeforeWorkflow += stackedWidget->currentWidget(); stackedWidget->setCurrentWidget(containingWidget); @@ -275,7 +276,7 @@ void AppQtMainWidget::workflowDeactivated() for (auto widget : qAsConst(mSelectedPagesBeforeWorkflow)) { - if (QStackedWidget* stackedWidget = qobject_cast(widget->parentWidget())) + if (auto* stackedWidget = qobject_cast(widget->parentWidget())) { stackedWidget->setCurrentWidget(widget); } @@ -492,7 +493,7 @@ void AppQtMainWidget::onTabButtonToggled(QAbstractButton* pButton, bool pChecked void AppQtMainWidget::onTabActionTriggered() { - if (QAction* action = qobject_cast(sender())) + if (auto* action = qobject_cast(sender())) { if (QAbstractButton* button = mTabAction2Button.value(action)) { @@ -542,7 +543,7 @@ void AppQtMainWidget::onContentActionClicked() QString name = mUi->stackedWidget->widget(mUi->stackedWidget->currentIndex())->objectName(); if (name.startsWith(QLatin1String("settingsPage"))) { - SettingsWidget* settingsWidget = static_cast(mUi->stackedWidget->widget(mUi->stackedWidget->currentIndex())); + auto* settingsWidget = static_cast(mUi->stackedWidget->widget(mUi->stackedWidget->currentIndex())); HelpAction::openContextHelp(settingsWidget->getActiveTabObjectName()); } else @@ -554,7 +555,7 @@ void AppQtMainWidget::onContentActionClicked() void AppQtMainWidget::onAboutActionClicked() { - AboutDialog* dialog = new AboutDialog(this); + auto* dialog = new AboutDialog(this); dialog->show(); } diff --git a/src/ui/widget/AppQtMainWidget.h b/src/ui/widget/AppQtMainWidget.h index 5cec676..aed709a 100644 --- a/src/ui/widget/AppQtMainWidget.h +++ b/src/ui/widget/AppQtMainWidget.h @@ -104,9 +104,11 @@ class AppQtMainWidget void fireCloseWindowRequested(bool* pDoClose); void fireSelfAuthenticationRequested(); void fireQuitApplicationRequested(); + void fireRestartApplicationRequested(); void fireChangeHighContrast(bool* pHighContrastOn); void fireAskUserToConfirmClosing(); void fireCloseActiveDialogs(); + void fireSwitchUiRequested(); private: QScopedPointer mUi; diff --git a/src/ui/widget/DeleteHistoryDialog.cpp b/src/ui/widget/DeleteHistoryDialog.cpp index 8f39c06..373076f 100644 --- a/src/ui/widget/DeleteHistoryDialog.cpp +++ b/src/ui/widget/DeleteHistoryDialog.cpp @@ -43,7 +43,7 @@ DeleteHistoryDialog::DeleteHistoryDialog(QWidget* pParent) mComboBox->addItem(tr("Last four weeks"), static_cast(TimePeriod::LAST_FOUR_WEEKS)); mComboBox->addItem(tr("All history"), static_cast(TimePeriod::ALL_HISTORY)); - QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); + auto* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); layout()->addItem(verticalSpacer); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); @@ -55,7 +55,7 @@ DeleteHistoryDialog::DeleteHistoryDialog(QWidget* pParent) QRadioButton* DeleteHistoryDialog::createRadioButtonAndAppendToGroup(const QString& pText, TimePeriod pTimePeriod) { - QRadioButton* button = new QRadioButton(this); + auto* button = new QRadioButton(this); button->setText(pText); button->setProperty("TimePeriod", Enum::getValue(pTimePeriod)); button->setObjectName(QStringLiteral("button%1").arg(QString::number(mRadioButtonGroup->buttons().count()))); diff --git a/src/ui/widget/DetailDialog.cpp b/src/ui/widget/DetailDialog.cpp index 4adafb9..d21dfb1 100644 --- a/src/ui/widget/DetailDialog.cpp +++ b/src/ui/widget/DetailDialog.cpp @@ -47,7 +47,7 @@ bool DetailDialog::eventFilter(QObject* pObject, QEvent* pEvent) { if (pEvent->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(pEvent); + auto* keyEvent = static_cast(pEvent); if (keyEvent->key() == Qt::Key_F1) { HelpAction::openContextHelp(); diff --git a/src/ui/widget/DeveloperSettingsWidget.ui b/src/ui/widget/DeveloperSettingsWidget.ui index 9e940f8..f623471 100644 --- a/src/ui/widget/DeveloperSettingsWidget.ui +++ b/src/ui/widget/DeveloperSettingsWidget.ui @@ -38,7 +38,7 @@ Qt::TabFocus - Self authentication test URI: + Self-authentication test URI: diff --git a/src/ui/widget/DiagnosisDialog.cpp b/src/ui/widget/DiagnosisDialog.cpp index 17dafd0..6bbe315 100644 --- a/src/ui/widget/DiagnosisDialog.cpp +++ b/src/ui/widget/DiagnosisDialog.cpp @@ -19,7 +19,7 @@ using namespace governikus; DiagnosisDialog::DiagnosisDialog(const QSharedPointer& pContext, QWidget* pParent) : QDialog(pParent) , mUi(new Ui::DiagnosisDialog) - , mDiagnosisModel(new DiagnosisModel(pContext)) + , mDiagnosisModel(new DiagnosisTreeModel(pContext)) , mTreeView(new QTreeView(this)) { mUi->setupUi(this); @@ -84,7 +84,7 @@ bool DiagnosisDialog::eventFilter(QObject* pObject, QEvent* pEvent) { if (pEvent->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(pEvent); + auto* keyEvent = static_cast(pEvent); if (keyEvent->key() == Qt::Key_F1) { HelpAction::openContextHelp(); diff --git a/src/ui/widget/DiagnosisDialog.h b/src/ui/widget/DiagnosisDialog.h index 6e06603..3b2e116 100644 --- a/src/ui/widget/DiagnosisDialog.h +++ b/src/ui/widget/DiagnosisDialog.h @@ -6,7 +6,7 @@ #pragma once -#include "DiagnosisModel.h" +#include "DiagnosisTreeModel.h" #include #include @@ -30,7 +30,7 @@ class DiagnosisDialog private: QScopedPointer mUi; - QScopedPointer mDiagnosisModel; + QScopedPointer mDiagnosisModel; QTreeView* mTreeView; private Q_SLOTS: diff --git a/src/ui/widget/DiagnosisGui.cpp b/src/ui/widget/DiagnosisGui.cpp index cfd3f48..eb1fb76 100644 --- a/src/ui/widget/DiagnosisGui.cpp +++ b/src/ui/widget/DiagnosisGui.cpp @@ -38,7 +38,7 @@ void DiagnosisGui::activate() } QWidget* dialogParent = qobject_cast(parent()); - if (!dialogParent) + if (dialogParent == nullptr) { return; } diff --git a/src/ui/widget/GeneralSettingsWidget.cpp b/src/ui/widget/GeneralSettingsWidget.cpp index a34d9f1..8e70e7e 100644 --- a/src/ui/widget/GeneralSettingsWidget.cpp +++ b/src/ui/widget/GeneralSettingsWidget.cpp @@ -42,7 +42,8 @@ GeneralSettingsWidget::GeneralSettingsWidget(QWidget* pParent) connect(mUi->regularlyUpdateCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); connect(mUi->closeWindowCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); connect(mUi->saveHistoryCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); - connect(mUi->updateCheckButton, &QCheckBox::clicked, this, &GeneralSettingsWidget::onUpdateCheckButtonClicked); + connect(mUi->updateCheckButton, &QPushButton::clicked, this, &GeneralSettingsWidget::onUpdateCheckButtonClicked); + connect(mUi->switchToBetaUiButton, &QPushButton::clicked, this, &GeneralSettingsWidget::fireSwitchUiRequested); connect(mUi->keylessPasswordCheckBox, &QCheckBox::stateChanged, this, &GeneralSettingsWidget::onCheckBoxStateChanged); } diff --git a/src/ui/widget/GeneralSettingsWidget.h b/src/ui/widget/GeneralSettingsWidget.h index 942d688..ec8ae10 100644 --- a/src/ui/widget/GeneralSettingsWidget.h +++ b/src/ui/widget/GeneralSettingsWidget.h @@ -44,6 +44,7 @@ class GeneralSettingsWidget Q_SIGNALS: void settingsChanged(); + void fireSwitchUiRequested(); }; } // namespace governikus diff --git a/src/ui/widget/GeneralSettingsWidget.ui b/src/ui/widget/GeneralSettingsWidget.ui index fad6039..90f4863 100644 --- a/src/ui/widget/GeneralSettingsWidget.ui +++ b/src/ui/widget/GeneralSettingsWidget.ui @@ -194,6 +194,52 @@ + + + + Qt::Horizontal + + + + + + + Qt::TabFocus + + + Try out the new beta UI: + + + + + + + true + + + + 0 + 0 + + + + Switch UI + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/ui/widget/HistoryWidget.cpp b/src/ui/widget/HistoryWidget.cpp index 3fa21fe..d6b3fde 100644 --- a/src/ui/widget/HistoryWidget.cpp +++ b/src/ui/widget/HistoryWidget.cpp @@ -17,7 +17,12 @@ #include "generic/ListItemTitle.h" #include "LanguageLoader.h" #include "PdfExporter.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include +#else #include "ScopeGuard.h" +#endif #include #include @@ -100,7 +105,7 @@ void HistoryWidget::updateTable() { const auto& items = Env::getSingleton()->getHistorySettings().getHistoryInfos(); - const ScopeGuard guard([this] { + const auto guard = qScopeGuard([this] { mUi->historyTableWidget->setUpdatesEnabled(true); }); @@ -129,7 +134,7 @@ void HistoryWidget::updateTable() //details column with needed properties QWidget* centralWidget = new QWidget(); - QFormLayout* centralLayout = new QFormLayout(centralWidget); + auto* centralLayout = new QFormLayout(centralWidget); centralLayout->setSpacing(6); centralLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); @@ -178,7 +183,7 @@ bool HistoryWidget::eventFilter(QObject* pObject, QEvent* pEvent) { if (pEvent->type() == QEvent::KeyPress) { - QKeyEvent* pressed = static_cast(pEvent); + auto* pressed = static_cast(pEvent); if ((pressed->key() == Qt::Key_Enter) || (pressed->key() == Qt::Key_Return) || (pressed->key() == Qt::Key_Space)) { const auto selectedIndexes = mUi->historyTableWidget->selectionModel()->selectedIndexes(); @@ -199,7 +204,7 @@ bool HistoryWidget::eventFilter(QObject* pObject, QEvent* pEvent) void HistoryWidget::deleteHistory() { - DeleteHistoryDialog* deleteHistoryDialog = new DeleteHistoryDialog(this); + auto* deleteHistoryDialog = new DeleteHistoryDialog(this); if (deleteHistoryDialog->exec() == QDialog::Rejected) { return; diff --git a/src/ui/widget/LogFilesDialog.cpp b/src/ui/widget/LogFilesDialog.cpp index 6bb0611..73a9039 100644 --- a/src/ui/widget/LogFilesDialog.cpp +++ b/src/ui/widget/LogFilesDialog.cpp @@ -173,7 +173,7 @@ bool LogFilesDialog::eventFilter(QObject* pObject, QEvent* pEvent) { if (pEvent->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(pEvent); + auto* keyEvent = static_cast(pEvent); if (keyEvent->key() == Qt::Key_F1) { HelpAction::openContextHelp(); diff --git a/src/ui/widget/PinSettingsWidget.cpp b/src/ui/widget/PinSettingsWidget.cpp index 331a298..5c03234 100644 --- a/src/ui/widget/PinSettingsWidget.cpp +++ b/src/ui/widget/PinSettingsWidget.cpp @@ -712,20 +712,13 @@ void PinSettingsWidget::setupChangePinHeader(int pRetryCounter, bool pIsBasicRea break; default: - if (mMode == Mode::AfterPinUnblock) + if (pIsBasicReader) { - mUi->headerStackedWidget->setCurrentWidget(mUi->pinUnblockedHeaderPage); + mUi->headerStackedWidget->setCurrentWidget(mUi->changePinBasicHeaderPage); } else { - if (pIsBasicReader) - { - mUi->headerStackedWidget->setCurrentWidget(mUi->changePinBasicHeaderPage); - } - else - { - mUi->headerStackedWidget->setCurrentWidget(mUi->changePinComfortHeaderPage); - } + mUi->headerStackedWidget->setCurrentWidget(mUi->changePinComfortHeaderPage); } break; } @@ -751,7 +744,7 @@ void PinSettingsWidget::onRandomPinButtonClicked() mRandomPinDialog = new RandomPinDialog(6, selectedReaderName, this); if (mRandomPinDialog->exec() == QDialog::Accepted && !mRandomPinDialog->getPin().isEmpty()) { - QToolButton* pinButton = qobject_cast(sender()); + auto* pinButton = qobject_cast(sender()); if (pinButton == nullptr) { qCCritical(gui) << "sender == nullptr"; diff --git a/src/ui/widget/PinSettingsWidget.h b/src/ui/widget/PinSettingsWidget.h index e23bb20..2cbe45d 100644 --- a/src/ui/widget/PinSettingsWidget.h +++ b/src/ui/widget/PinSettingsWidget.h @@ -33,8 +33,7 @@ class PinSettingsWidget enum class Mode { Normal, - AfterPinChange, - AfterPinUnblock, + AfterPinChange }; public: diff --git a/src/ui/widget/PinSettingsWidget.ui b/src/ui/widget/PinSettingsWidget.ui index 0e4d299..52ca4a0 100644 --- a/src/ui/widget/PinSettingsWidget.ui +++ b/src/ui/widget/PinSettingsWidget.ui @@ -183,58 +183,6 @@ Please note that you can only use the PUK to unblock the eID function. If you ha - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 20 - - - 20 - - - - - - - - :/images/Icon_Checked.svg - - - - - - - Qt::TabFocus - - - <h4>PUK entry successful</h4><p>Your ID card is unblocked. You now have three more tries to change your PIN.</p> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - true - - - - - - - diff --git a/src/ui/widget/RandomPinDialog.cpp b/src/ui/widget/RandomPinDialog.cpp index dfdb170..fff864d 100644 --- a/src/ui/widget/RandomPinDialog.cpp +++ b/src/ui/widget/RandomPinDialog.cpp @@ -99,7 +99,7 @@ QString RandomPinDialog::getPin() void RandomPinDialog::onPosButtonClicked() { - QToolButton* posButton = qobject_cast(sender()); + auto* posButton = qobject_cast(sender()); if (posButton) { mUi->pin->setText(mUi->pin->text() + posButton->property(PIN).toString()); @@ -129,7 +129,7 @@ bool RandomPinDialog::eventFilter(QObject* pObject, QEvent* pEvent) { if (pEvent->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(pEvent); + auto* keyEvent = static_cast(pEvent); if (keyEvent->key() == Qt::Key_F1) { HelpAction::openContextHelp(); diff --git a/src/ui/widget/ReaderDeviceDialog.cpp b/src/ui/widget/ReaderDeviceDialog.cpp index ef3f5b8..ecb4747 100644 --- a/src/ui/widget/ReaderDeviceDialog.cpp +++ b/src/ui/widget/ReaderDeviceDialog.cpp @@ -40,7 +40,7 @@ bool ReaderDeviceDialog::eventFilter(QObject* pObject, QEvent* pEvent) { if (pEvent->type() == QEvent::KeyPress) { - QKeyEvent* const keyEvent = static_cast(pEvent); + auto* const keyEvent = static_cast(pEvent); if (keyEvent->key() == Qt::Key_F1) { HelpAction::openContextHelp(); diff --git a/src/ui/widget/ReaderDeviceGui.cpp b/src/ui/widget/ReaderDeviceGui.cpp index fa04c47..e9e03c2 100644 --- a/src/ui/widget/ReaderDeviceGui.cpp +++ b/src/ui/widget/ReaderDeviceGui.cpp @@ -24,10 +24,10 @@ ReaderDeviceGui::~ReaderDeviceGui() void ReaderDeviceGui::activate() { - if (!mDialog) + if (mDialog == nullptr) { QWidget* dialogParent = qobject_cast(parent()); - if (!dialogParent) + if (dialogParent == nullptr) { return; } @@ -42,7 +42,7 @@ void ReaderDeviceGui::activate() void ReaderDeviceGui::deactivate() { - if (mDialog) + if (mDialog != nullptr) { mDialog->close(); } @@ -51,7 +51,7 @@ void ReaderDeviceGui::deactivate() void ReaderDeviceGui::reactToReaderCount(int pReaderCount) { - if (mDialog && pReaderCount > 0) + if (mDialog != nullptr && pReaderCount > 0) { mDialog->close(); } @@ -75,7 +75,7 @@ void ReaderDeviceGui::reactivate() void ReaderDeviceGui::onFinished(int result) { - Q_UNUSED(result); + Q_UNUSED(result) mDialog = nullptr; } diff --git a/src/ui/widget/ReaderDeviceWidget.cpp b/src/ui/widget/ReaderDeviceWidget.cpp index 2697c68..05167f0 100644 --- a/src/ui/widget/ReaderDeviceWidget.cpp +++ b/src/ui/widget/ReaderDeviceWidget.cpp @@ -5,8 +5,6 @@ #include "ReaderDeviceWidget.h" #include "ui_ReaderDeviceWidget.h" -#include "HelpAction.h" -#include "LanguageLoader.h" #include "ReaderConfiguration.h" #include "RemoteClient.h" #include "RemotePinInputDialog.h" @@ -60,7 +58,7 @@ ReaderDeviceWidget::ReaderDeviceWidget(QWidget* pParent) connect(mUi->forgetRemote, &QPushButton::clicked, this, &ReaderDeviceWidget::onForgetClicked); connect(mUi->tableViewRemote, &QTableView::doubleClicked, this, &ReaderDeviceWidget::onRemoteDoubleClicked); - RemoteClient* const remoteClient = Env::getSingleton(); + auto* const remoteClient = Env::getSingleton(); connect(remoteClient, &RemoteClient::fireDispatcherDestroyed, &mRemoteReaderDataModel, &RemoteDeviceModel::onDeviceDisconnected); } @@ -113,15 +111,11 @@ void ReaderDeviceWidget::onUpdateInfo() void ReaderDeviceWidget::setDisplayText() { - const QString& url = HelpAction::getOnlineUrl(QStringLiteral("readerDeviceTab")); - //: Is embedded in a sentence. - const QString hyperlink = QStringLiteral("%2").arg(url, tr("online help")); - - const QString remoteEmptyListDescriptionString = tr("No smartphone with enabled remote service found. See %1 for details of use.").arg(hyperlink); + const QString& remoteEmptyListDescriptionString = mRemoteReaderDataModel.getEmptyListDescriptionString(); mUi->remoteEmptyListDescription->setText(remoteEmptyListDescriptionString); mUi->remoteEmptyListDescription->setAccessibleName(remoteEmptyListDescriptionString); - const QString localEmptyListDescriptionString = tr("No connected card reader found. See %1 for installation of card readers.").arg(hyperlink); + const QString& localEmptyListDescriptionString = mLocalReaderDataModel.getEmptyListDescriptionString(); mUi->localEmptyListDescription->setText(localEmptyListDescriptionString); mUi->localEmptyListDescription->setAccessibleDescription(localEmptyListDescriptionString); } @@ -135,12 +129,12 @@ void ReaderDeviceWidget::updateInfoIcon() QPixmap pixmap; if (selectionList.isEmpty()) { - pixmap = QPixmap(ReaderConfiguration::getNoReaderFoundIconPath()); + pixmap = QPixmap(mLocalReaderDataModel.getNoReaderFoundIconPath()); } else { const QModelIndex& index = selectionList.at(0); - const QString& path = mLocalReaderDataModel.getReaderConfigurationInfo(index).getIcon()->lookupPath(); + const QString& path = mLocalReaderDataModel.getReaderImagePath(index); pixmap = QPixmap(path); if (mLocalReaderDataModel.isInstalledSupportedReader(index)) { @@ -167,7 +161,7 @@ void ReaderDeviceWidget::updateInfoText() { if (mLocalReaderDataModel.rowCount() == 0) { - infoText = tr("Please connect suitable card reader."); + infoText = tr("Please connect a suitable card reader."); } else { @@ -194,10 +188,7 @@ void ReaderDeviceWidget::updateInfoText() void ReaderDeviceWidget::updateInfoUpdate() { - const auto& now = LanguageLoader::getInstance().getUsedLocale().toString(QTime::currentTime(), tr("hh:mm:ss AP")); - const QString& text = tr("The list of card readers was last updated at %1."); - - mUi->updateTimeLabel->setText(text.arg(now)); + mUi->updateTimeLabel->setText(mLocalReaderDataModel.getLastUpdatedInformation()); } @@ -310,7 +301,7 @@ void ReaderDeviceWidget::onConnectClicked() const QString pin = RemotePinInputDialog::getPin(this); if (!pin.isEmpty()) { - RemoteClient* const remoteClient = Env::getSingleton(); + auto* const remoteClient = Env::getSingleton(); connect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &ReaderDeviceWidget::onEstablishConnectionDone); remoteClient->establishConnection(remoteDeviceListEntry, pin); } @@ -322,8 +313,8 @@ void ReaderDeviceWidget::onConnectClicked() void ReaderDeviceWidget::onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus) { - Q_UNUSED(pEntry); - RemoteClient* const remoteClient = Env::getSingleton(); + Q_UNUSED(pEntry) + auto* const remoteClient = Env::getSingleton(); disconnect(remoteClient, &RemoteClient::fireEstablishConnectionDone, this, &ReaderDeviceWidget::onEstablishConnectionDone); if (pStatus.isError()) { diff --git a/src/ui/widget/ReaderDriverModel.cpp b/src/ui/widget/ReaderDriverModel.cpp deleted file mode 100644 index 89d3a93..0000000 --- a/src/ui/widget/ReaderDriverModel.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/*! - * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "ReaderDriverModel.h" - -#include "AppSettings.h" -#include "ReaderConfiguration.h" -#include "ReaderDetector.h" -#include "ReaderManager.h" - - -using namespace governikus; - - -ReaderDriverModel::ReaderDriverModel(QObject* pParent) - : QAbstractTableModel(pParent) - , mKnownDrivers() - , mConnectedReaders() -{ - const ReaderManager* const readerManager = Env::getSingleton(); - - connect(readerManager, &ReaderManager::fireReaderAdded, this, &ReaderDriverModel::onUpdateContent); - connect(readerManager, &ReaderManager::fireReaderRemoved, this, &ReaderDriverModel::onUpdateContent); - connect(Env::getSingleton(), &ReaderConfiguration::fireUpdated, this, &ReaderDriverModel::onUpdateContent); - connect(Env::getSingleton(), &ReaderDetector::fireReaderChangeDetected, this, &ReaderDriverModel::onUpdateContent); - connect(Env::getSingleton(), &AppSettings::fireSettingsChanged, this, &ReaderDriverModel::onUpdateContent); - onUpdateContent(); -} - - -void ReaderDriverModel::collectReaderData() -{ - mConnectedReaders.clear(); - - const QVector installedReaders = Env::getSingleton()->getReaderInfos(ReaderFilter({ - ReaderManagerPlugInType::PCSC - , ReaderManagerPlugInType::BLUETOOTH - , ReaderManagerPlugInType::NFC - })); - - for (const auto& installedReader : installedReaders) - { - const auto& readerSettingsInfo = installedReader.getReaderConfigurationInfo(); - if (!readerSettingsInfo.getUrl().isEmpty()) - { - mKnownDrivers += readerSettingsInfo; - mConnectedReaders += readerSettingsInfo; - } - } - - QVector readersWithoutDriver; - const auto& attachedSupportedDevices = Env::getSingleton()->getAttachedSupportedDevices(); - for (const auto& info : attachedSupportedDevices) - { - if (!mConnectedReaders.contains(info)) - { - readersWithoutDriver.append(info); - } - } - mConnectedReaders += readersWithoutDriver; -} - - -QString ReaderDriverModel::getStatus(const ReaderConfigurationInfo& pReaderConfigurationInfo) const -{ - if (mConnectedReaders.isEmpty()) - { - return tr("Not connected"); - } - - if (mKnownDrivers.contains(pReaderConfigurationInfo)) - { - return tr("Driver installed"); - } - - return tr("No driver installed"); -} - - -void ReaderDriverModel::onUpdateContent() -{ - beginResetModel(); - collectReaderData(); - endResetModel(); - - Q_EMIT fireModelChanged(); -} - - -QVariant ReaderDriverModel::headerData(int pSection, Qt::Orientation pOrientation, int pRole) const -{ - if (pRole == Qt::DisplayRole && pOrientation == Qt::Horizontal) - { - switch (pSection) - { - case ColumnId::ReaderName: - return tr("Device"); - - case ColumnId::ReaderStatus: - return tr("Status"); - - default: - return QVariant(); - } - } - return QVariant(); -} - - -int ReaderDriverModel::rowCount(const QModelIndex&) const -{ - return mConnectedReaders.size(); -} - - -int ReaderDriverModel::columnCount(const QModelIndex&) const -{ - return NUMBER_OF_COLUMNS; -} - - -QVariant ReaderDriverModel::data(const QModelIndex& pIndex, int pRole) const -{ - if (pRole == Qt::DisplayRole) - { - const auto& reader = mConnectedReaders.at(pIndex.row()); - switch (pIndex.column()) - { - case ColumnId::ReaderName: - return reader.getName(); - - case ColumnId::ReaderStatus: - return getStatus(reader); - } - } - - return QVariant(); -} - - -const ReaderConfigurationInfo& ReaderDriverModel::getReaderConfigurationInfo(const QModelIndex& pIndex) const -{ - return mConnectedReaders.at(pIndex.row()); -} - - -QString ReaderDriverModel::getHTMLDescription(const QModelIndex& pIndex) const -{ - if (mConnectedReaders.isEmpty()) - { - return QString(); - } - - if (mKnownDrivers.contains(mConnectedReaders.at(pIndex.row()))) - { - return tr("Card reader ready for use."); - } - - return tr("Please download and install the driver you can find at: %1"). - arg(QStringLiteral("%1").arg(mConnectedReaders.at(pIndex.row()).getUrl())); -} - - -bool ReaderDriverModel::isInstalledSupportedReader(const QModelIndex& pIndex) const -{ - const auto& readerSettingsInfo = mConnectedReaders.at(pIndex.row()); - const bool knownDriver = mKnownDrivers.contains(readerSettingsInfo); - const bool knownReader = readerSettingsInfo.isKnownReader(); - return knownDriver && knownReader; -} diff --git a/src/ui/widget/RemotePinInputDialog.cpp b/src/ui/widget/RemotePinInputDialog.cpp index a45c849..dc6ac75 100644 --- a/src/ui/widget/RemotePinInputDialog.cpp +++ b/src/ui/widget/RemotePinInputDialog.cpp @@ -40,8 +40,7 @@ RemotePinInputDialog::~RemotePinInputDialog() const QString RemotePinInputDialog::getPin(QWidget* pParent) { RemotePinInputDialog dialog(pParent); - int result = dialog.exec(); - if (!result) + if (dialog.exec() == QDialog::Rejected) { return QString(); } diff --git a/src/ui/widget/RemotePinInputDialog.h b/src/ui/widget/RemotePinInputDialog.h index e0a5c1a..f7c726e 100644 --- a/src/ui/widget/RemotePinInputDialog.h +++ b/src/ui/widget/RemotePinInputDialog.h @@ -28,7 +28,7 @@ class RemotePinInputDialog void onOkClicked(); public: - RemotePinInputDialog(QWidget* pParent = 0); + RemotePinInputDialog(QWidget* pParent = nullptr); virtual ~RemotePinInputDialog() override; static const QString getPin(QWidget* pParent); diff --git a/src/ui/widget/SettingsWidget.cpp b/src/ui/widget/SettingsWidget.cpp index 66cdd92..b88a681 100644 --- a/src/ui/widget/SettingsWidget.cpp +++ b/src/ui/widget/SettingsWidget.cpp @@ -28,6 +28,7 @@ SettingsWidget::SettingsWidget(QWidget* pParent) connect(mUi->diagnosisButton, &QAbstractButton::clicked, this, &SettingsWidget::diagnosisRequested); connect(mUi->generalTab, &GeneralSettingsWidget::settingsChanged, this, &SettingsWidget::onSettingsChanged); + connect(mUi->generalTab, &GeneralSettingsWidget::fireSwitchUiRequested, this, &SettingsWidget::fireSwitchUiRequested); connect(mUi->pinTab, &PinSettingsWidget::fireButtonEnabledUpdated, this, &SettingsWidget::onUpdateButtonState); connect(mUi->pinTab, &PinSettingsWidget::fireButtonEnabledUpdated, this, &SettingsWidget::onUpdateApplyButtonText); diff --git a/src/ui/widget/SettingsWidget.h b/src/ui/widget/SettingsWidget.h index 85c74f5..a6c5c4b 100644 --- a/src/ui/widget/SettingsWidget.h +++ b/src/ui/widget/SettingsWidget.h @@ -84,6 +84,7 @@ class SettingsWidget void diagnosisRequested(); void settingsDone(); void fireBackspacePressedOnApply(); + void fireSwitchUiRequested(); }; } // namespace governikus diff --git a/src/ui/widget/SetupAssistantGui.cpp b/src/ui/widget/SetupAssistantGui.cpp index a2af51e..66246e8 100644 --- a/src/ui/widget/SetupAssistantGui.cpp +++ b/src/ui/widget/SetupAssistantGui.cpp @@ -30,7 +30,7 @@ void SetupAssistantGui::activate() if (!mWizard) { QWidget* dialogParent = qobject_cast(parent()); - if (!dialogParent) + if (dialogParent == nullptr) { return; } diff --git a/src/ui/widget/SetupAssistantWizard.cpp b/src/ui/widget/SetupAssistantWizard.cpp index 756a94d..426d037 100644 --- a/src/ui/widget/SetupAssistantWizard.cpp +++ b/src/ui/widget/SetupAssistantWizard.cpp @@ -27,7 +27,7 @@ CardReaderPage::CardReaderPage(const QString& pTitle, const QString& pAccessible setTitle(pTitle); mWidget->prependAccessibleName(pAccessibleName); - QVBoxLayout* cardReaderPageVLayout = new QVBoxLayout(this); + auto* cardReaderPageVLayout = new QVBoxLayout(this); cardReaderPageVLayout->addWidget(mWidget); } @@ -94,7 +94,7 @@ QString SetupAssistantWizard::createDescription(const QString& pTitle, const QSt QWizardPage* SetupAssistantWizard::createWizardInitialPinPage() { - QWizardPage* initialPinPage = new QWizardPage; + auto* initialPinPage = new QWizardPage; const auto& introduction = tr("Introduction"); initialPinPage->setTitle(createTitle(introduction)); @@ -106,7 +106,7 @@ QWizardPage* SetupAssistantWizard::createWizardInitialPinPage() label->setFocusPolicy(Qt::TabFocus); label->setAccessibleName(createAccessibleName(introduction, welcome)); - QVBoxLayout* initialPinPageLayout = new QVBoxLayout; + auto* initialPinPageLayout = new QVBoxLayout; initialPinPageLayout->addWidget(label); const auto& historyTitle = tr("History"); @@ -126,7 +126,7 @@ QWizardPage* SetupAssistantWizard::createWizardInitialPinPage() saveHistorySizePolicy.setHeightForWidth(saveHistoryWidget->sizePolicy().hasHeightForWidth()); saveHistoryWidget->setSizePolicy(saveHistorySizePolicy); - QFormLayout* saveHistoryFormLayout = new QFormLayout(saveHistoryWidget); + auto* saveHistoryFormLayout = new QFormLayout(saveHistoryWidget); saveHistoryFormLayout->setHorizontalSpacing(30); saveHistoryFormLayout->setContentsMargins(11, 11, 11, 11); saveHistoryFormLayout->setContentsMargins(0, 5, 0, 20); @@ -161,11 +161,11 @@ QWizardPage* SetupAssistantWizard::createWizardCardReaderPage() QWizardPage* SetupAssistantWizard::createConclusionPage() { - QWizardPage* conclusionPage = new QWizardPage; + auto* conclusionPage = new QWizardPage; const auto& almostDone = tr("Almost done!"); conclusionPage->setTitle(createTitle(almostDone)); - QVBoxLayout* conclusionPageVLayout = new QVBoxLayout(conclusionPage); + auto* conclusionPageVLayout = new QVBoxLayout(conclusionPage); const auto& title = tr("Personal 6 - digit PIN"); const auto& desc = tr("Prior to the first use of the online identification function, you have to replace the transport PIN by an individual 6-digit PIN. " @@ -202,7 +202,7 @@ QWizardPage* SetupAssistantWizard::createConclusionPage() conclusionPageVLayout->addWidget(conclusionDescLabel); - QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + auto* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); conclusionPageVLayout->addItem(verticalSpacer); return conclusionPage; @@ -232,7 +232,7 @@ bool SetupAssistantWizard::eventFilter(QObject* pObject, QEvent* pEvent) { if (pEvent->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(pEvent); + auto* keyEvent = static_cast(pEvent); if (keyEvent->key() == Qt::Key_F1) { HelpAction::openContextHelp(objectName()); diff --git a/src/ui/widget/UIPlugInWidgets.cpp b/src/ui/widget/UIPlugInWidgets.cpp index e754288..c3a87b6 100644 --- a/src/ui/widget/UIPlugInWidgets.cpp +++ b/src/ui/widget/UIPlugInWidgets.cpp @@ -18,6 +18,7 @@ UIPlugInWidgets::UIPlugInWidgets() connect(&mGui, &AppQtGui::fireChangePinRequested, this, &UIPlugIn::fireChangePinRequest); connect(&mGui, &AppQtGui::selfAuthenticationRequested, this, &UIPlugIn::fireSelfAuthenticationRequested); connect(&mGui, &AppQtGui::fireCloseReminderFinished, this, &UIPlugInWidgets::fireCloseReminderFinished); + connect(&mGui, &AppQtGui::fireRestartApplicationRequested, this, &UIPlugIn::fireRestartApplicationRequested); connect(this, &UIPlugIn::fireShowUserInformation, &mGui, &AppQtGui::onShowUserInformation); mGui.init(); } diff --git a/src/ui/widget/generic/BusyOverlay.cpp b/src/ui/widget/generic/BusyOverlay.cpp index bbfec46..bea53fb 100644 --- a/src/ui/widget/generic/BusyOverlay.cpp +++ b/src/ui/widget/generic/BusyOverlay.cpp @@ -25,7 +25,7 @@ BusyOverlay::BusyOverlay(bool pStart, QWidget* pParent) setStyleSheet(QStringLiteral("background-color: white;")); - QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect(this); + auto* opacity = new QGraphicsOpacityEffect(this); opacity->setOpacity(0.9); setGraphicsEffect(opacity); diff --git a/src/ui/widget/generic/BusyOverlayContainer.cpp b/src/ui/widget/generic/BusyOverlayContainer.cpp index 3e42ce9..b99bdf3 100644 --- a/src/ui/widget/generic/BusyOverlayContainer.cpp +++ b/src/ui/widget/generic/BusyOverlayContainer.cpp @@ -19,7 +19,7 @@ BusyOverlayContainer::BusyOverlayContainer(QWidget* pWidgetToOverlay, bool pStar QBoxLayout* overlayContainerLayout = new QVBoxLayout(busyOverlayContainer); overlayContainerLayout->addWidget(mOverlay, 0, Qt::AlignHCenter | Qt::AlignVCenter); - QStackedLayout* stackLayout = qobject_cast(layout()); + auto* stackLayout = qobject_cast(layout()); stackLayout->setStackingMode(QStackedLayout::StackAll); stackLayout->addWidget(pWidgetToOverlay); diff --git a/src/ui/widget/generic/ExclusiveButtonGroup.cpp b/src/ui/widget/generic/ExclusiveButtonGroup.cpp index 0ac3293..31cf3c1 100644 --- a/src/ui/widget/generic/ExclusiveButtonGroup.cpp +++ b/src/ui/widget/generic/ExclusiveButtonGroup.cpp @@ -51,7 +51,7 @@ void ExclusiveButtonGroup::removeButton(QAbstractButton* pButton) bool ExclusiveButtonGroup::eventFilter(QObject* pWatched, QEvent* pEvent) { - if (QAbstractButton* button = qobject_cast(pWatched)) + if (auto* button = qobject_cast(pWatched)) { if (pEvent->type() == QEvent::MouseButtonPress && button->isChecked()) { @@ -60,7 +60,7 @@ bool ExclusiveButtonGroup::eventFilter(QObject* pWatched, QEvent* pEvent) if (pEvent->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(pEvent); + auto* keyEvent = static_cast(pEvent); if (keyEvent->key() == Qt::Key_Select || keyEvent->key() == Qt::Key_Space) { return true; @@ -74,7 +74,7 @@ bool ExclusiveButtonGroup::eventFilter(QObject* pWatched, QEvent* pEvent) void ExclusiveButtonGroup::onButtonClicked(bool /*pChecked*/) { - if (QAbstractButton* button = qobject_cast(sender())) + if (auto* button = qobject_cast(sender())) { Q_EMIT buttonClicked(button); } @@ -83,7 +83,7 @@ void ExclusiveButtonGroup::onButtonClicked(bool /*pChecked*/) void ExclusiveButtonGroup::onButtonPressed() { - if (QAbstractButton* button = qobject_cast(sender())) + if (auto* button = qobject_cast(sender())) { Q_EMIT buttonPressed(button); } @@ -92,7 +92,7 @@ void ExclusiveButtonGroup::onButtonPressed() void ExclusiveButtonGroup::onButtonReleased() { - if (QAbstractButton* button = qobject_cast(sender())) + if (auto* button = qobject_cast(sender())) { Q_EMIT buttonReleased(button); } @@ -101,7 +101,7 @@ void ExclusiveButtonGroup::onButtonReleased() void ExclusiveButtonGroup::onButtonToggled(bool pChecked) { - if (QAbstractButton* button = qobject_cast(sender())) + if (auto* button = qobject_cast(sender())) { if (pChecked) { diff --git a/src/ui/widget/generic/GuiUtils.cpp b/src/ui/widget/generic/GuiUtils.cpp index 6408a88..51e0c7e 100644 --- a/src/ui/widget/generic/GuiUtils.cpp +++ b/src/ui/widget/generic/GuiUtils.cpp @@ -107,3 +107,20 @@ bool GuiUtils::showWrongPinBlockedDialog(QWidget* pParent) return messageBox.exec() == QMessageBox::Yes; } + + +bool GuiUtils::showPinUnlockedDialog(QWidget* pParent) +{ + QMessageBox messageBox(pParent); + + QString title = tr("PIN successfully unblocked"); + QString text = tr("Your ID card is unblocked. You now have three more tries to change your PIN"); + messageBox.setWindowTitle(QCoreApplication::applicationName() + QStringLiteral(" - ") + title); + messageBox.setWindowModality(Qt::WindowModal); + messageBox.setText(QStringLiteral("

%1

%2

").arg(title, text)); + messageBox.setIconPixmap(QIcon(QStringLiteral(":/images/Icon_Checked.svg")).pixmap(32, 32)); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.button(QMessageBox::Ok)->setFocus(); + + return messageBox.exec() == QMessageBox::Yes; +} diff --git a/src/ui/widget/generic/GuiUtils.h b/src/ui/widget/generic/GuiUtils.h index 0c62b45..34e2ce7 100644 --- a/src/ui/widget/generic/GuiUtils.h +++ b/src/ui/widget/generic/GuiUtils.h @@ -21,6 +21,7 @@ class GuiUtils public: static bool showPinCanPukErrorDialog(CardReturnCode pReturnCode, bool pCanAllowedMode, QWidget* pParent); static bool showWrongPinBlockedDialog(QWidget* pParent); + static bool showPinUnlockedDialog(QWidget* pParent); }; } // namespace governikus diff --git a/src/ui/widget/generic/PasswordEdit.cpp b/src/ui/widget/generic/PasswordEdit.cpp index 726c1da..5eeec94 100644 --- a/src/ui/widget/generic/PasswordEdit.cpp +++ b/src/ui/widget/generic/PasswordEdit.cpp @@ -5,7 +5,11 @@ #include "PasswordEdit.h" +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include +#else #include "ScopeGuard.h" +#endif #include #include @@ -40,7 +44,7 @@ class RegExValidator if (state == State::Invalid && !mInvalidValueToolTip.isNull()) { - QWidget* parentWidget = static_cast(parent()); + auto* parentWidget = static_cast(parent()); QToolTip::showText(parentWidget->mapToGlobal(QPoint(0, 0)), mInvalidValueToolTip, parentWidget, QRect(), 3000); } @@ -70,7 +74,7 @@ int PasswordEdit::determindeWidth(int pNumChars) QLineEdit* const lineEdit = mUi->lineEdit; const QString currentText = lineEdit->text(); - const ScopeGuard resetText([lineEdit, currentText] { + const auto resetText = qScopeGuard([lineEdit, currentText] { lineEdit->setText(currentText); }); diff --git a/src/ui/widget/generic/TabButtonGroup.cpp b/src/ui/widget/generic/TabButtonGroup.cpp index 2376560..9aef2a9 100644 --- a/src/ui/widget/generic/TabButtonGroup.cpp +++ b/src/ui/widget/generic/TabButtonGroup.cpp @@ -108,7 +108,7 @@ bool TabButtonGroup::eventFilter(QObject* pWatched, QEvent* pEvent) return false; } - QAbstractButton* button = qobject_cast(pWatched); + auto* button = qobject_cast(pWatched); if (button == nullptr) { return false; @@ -120,7 +120,7 @@ bool TabButtonGroup::eventFilter(QObject* pWatched, QEvent* pEvent) bool cycle = false; bool next = true; - QKeyEvent* keyEvent = static_cast(pEvent); + auto* keyEvent = static_cast(pEvent); switch (keyEvent->key()) { case Qt::Key_Up: diff --git a/src/ui/widget/step/StepAuthenticationEac1Gui.cpp b/src/ui/widget/step/StepAuthenticationEac1Gui.cpp index 4a8124b..cb87f24 100644 --- a/src/ui/widget/step/StepAuthenticationEac1Gui.cpp +++ b/src/ui/widget/step/StepAuthenticationEac1Gui.cpp @@ -124,24 +124,22 @@ void StepAuthenticationEac1Gui::onPinUpdated(const QString& pPin) { if (mContext->isCanAllowedMode()) { - mCan = pPin; + mContext->setCan(pPin); } else { - mPin = pPin; + mContext->setPin(pPin); } } void StepAuthenticationEac1Gui::onCanUpdated(const QString& pCan) { - mCan = pCan; + mContext->setCan(pCan); } void StepAuthenticationEac1Gui::onUiFinished() { - mContext->setCan(mCan); - mContext->setPin(mPin); mContext->setStateApproved(); } diff --git a/src/ui/widget/step/StepAuthenticationEac1Gui.h b/src/ui/widget/step/StepAuthenticationEac1Gui.h index 63ece51..99a1b2a 100644 --- a/src/ui/widget/step/StepAuthenticationEac1Gui.h +++ b/src/ui/widget/step/StepAuthenticationEac1Gui.h @@ -27,8 +27,6 @@ class StepAuthenticationEac1Gui StepAuthenticationEac1Widget* mWidget; StepAuthenticationEac1Widget::State mState; QPointer mPayAttentionToReaderMsgBox; - QString mPin; - QString mCan; bool mActive; public: diff --git a/src/ui/widget/step/StepAuthenticationEac1Widget.cpp b/src/ui/widget/step/StepAuthenticationEac1Widget.cpp index 38bdd80..fdfde69 100644 --- a/src/ui/widget/step/StepAuthenticationEac1Widget.cpp +++ b/src/ui/widget/step/StepAuthenticationEac1Widget.cpp @@ -251,19 +251,19 @@ void StepAuthenticationEac1Widget::updateWidget() case State::AUTHENTICATING_ESERVICE: Q_EMIT setCancelButtonState(ButtonState::ENABLED); - updateProgressPanel(1, tr("Service provider is verified")); + updateProgressPanel(); break; case State::AUTHENTICATING_CARD: - updateProgressPanel(2, tr("Card is being verified")); + updateProgressPanel(); break; case State::READING_CARD_DATA: - updateProgressPanel(3, tr("Reading data")); + updateProgressPanel(); break; case State::REDIRECTING_BROWSER: - updateProgressPanel(4, tr("Service provider is being verified")); + updateProgressPanel(); break; case State::FINISHED: @@ -345,7 +345,7 @@ void StepAuthenticationEac1Widget::addChatRightToGui(AccessRight pRight, bool pO { displayText += QStringLiteral(" (%1)").arg(mContext->getDidAuthenticateEac1()->getAuthenticatedAuxiliaryData()->getRequiredAge()); } - QCheckBox* cb = new QCheckBox(displayText); + auto* cb = new QCheckBox(displayText); cb->setEnabled(pOptional); cb->setChecked(mContext->getEffectiveAccessRights().contains(pRight)); @@ -353,7 +353,7 @@ void StepAuthenticationEac1Widget::addChatRightToGui(AccessRight pRight, bool pO connect(cb, &QCheckBox::stateChanged, this, &StepAuthenticationEac1Widget::checkBoxChanged); - QListWidgetItem* item = new QListWidgetItem(); + auto* item = new QListWidgetItem(); item->setSizeHint(QSize(0, 20)); item->setData(Qt::AccessibleTextRole, displayText); if (mUi->listWidgetWest->count() < pListSize) @@ -373,7 +373,7 @@ void StepAuthenticationEac1Widget::createBasicReaderWidget() { QWidget* basicReaderWidget = new QWidget(); - QHBoxLayout* basicReaderWidgetLayout = new QHBoxLayout(basicReaderWidget); + auto* basicReaderWidgetLayout = new QHBoxLayout(basicReaderWidget); const auto& allowedDigitsMsg = tr("Only digits (0-9) are permitted."); QRegularExpression onlyNumbersExpression(QStringLiteral("[0-9]*")); @@ -393,7 +393,7 @@ void StepAuthenticationEac1Widget::createBasicReaderWidget() if (Env::getSingleton()->getGeneralSettings().isUseScreenKeyboard()) { - QToolButton* button = new QToolButton(); + auto* button = new QToolButton(); button->setObjectName(QStringLiteral("canRandomButton")); button->setAccessibleName(tr("open on screen keyboard")); button->setAutoRaise(true); @@ -426,7 +426,7 @@ void StepAuthenticationEac1Widget::createBasicReaderWidget() if (Env::getSingleton()->getGeneralSettings().isUseScreenKeyboard()) { - QToolButton* button = new QToolButton(); + auto* button = new QToolButton(); button->setObjectName(QStringLiteral("pinRandomButton")); button->setAccessibleName(tr("open on screen keyboard")); button->setAutoRaise(true); @@ -443,21 +443,24 @@ void StepAuthenticationEac1Widget::createBasicReaderWidget() } -void StepAuthenticationEac1Widget::updateProgressPanel(int pProgressValue, const QString& pProgressText) +void StepAuthenticationEac1Widget::updateProgressPanel() { - if (pProgressValue > 0) + const int progressValue = mContext->getProgressValue(); + const QString& progressText = mContext->getProgressMessage(); + + if (progressValue > 0) { if (mProgressBar == nullptr) { clearPinWidgetLayout(); QWidget* progressWidget = new QWidget(); - QVBoxLayout* progressWidgetLayout = new QVBoxLayout(progressWidget); + auto* progressWidgetLayout = new QVBoxLayout(progressWidget); progressWidgetLayout->setMargin(0); mUi->pinWidgetLayout->addWidget(progressWidget); mProgressBar = new QProgressBar(); mProgressBar->setTextVisible(false); - mProgressBar->setRange(0, 4); + mProgressBar->setRange(0, 100); progressWidgetLayout->addWidget(mProgressBar); mProgressBarLabel = new QLabel(); @@ -467,15 +470,15 @@ void StepAuthenticationEac1Widget::updateProgressPanel(int pProgressValue, const mUi->pinGroupBox->setVisible(true); } - mProgressBar->setValue(pProgressValue); - mProgressBarLabel->setText(pProgressText); + mProgressBar->setValue(progressValue); + mProgressBarLabel->setText(progressText); } else { const bool cancelled = mContext->getStatus().isCancellationByUser(); clearPinWidgetLayout(); QWidget* doneWidget = new QWidget(); - QHBoxLayout* doneWidgetLayout = new QHBoxLayout(doneWidget); + auto* doneWidgetLayout = new QHBoxLayout(doneWidget); doneWidgetLayout->setMargin(0); mUi->pinWidgetLayout->addWidget(doneWidget); @@ -494,7 +497,7 @@ void StepAuthenticationEac1Widget::updateProgressPanel(int pProgressValue, const if (mTaskbarButton) { auto progress = mTaskbarButton->progress(); - progress->setValue(pProgressValue == 0 ? progress->maximum() : pProgressValue); + progress->setValue(progressValue == 0 ? progress->maximum() : progressValue); } #endif } @@ -502,7 +505,7 @@ void StepAuthenticationEac1Widget::updateProgressPanel(int pProgressValue, const void StepAuthenticationEac1Widget::checkBoxChanged(int pCheckState) { - QCheckBox* cb = qobject_cast(sender()); + auto* cb = qobject_cast(sender()); if (cb != nullptr) { if (pCheckState == Qt::Unchecked) @@ -526,7 +529,7 @@ void StepAuthenticationEac1Widget::onRandomButtonClicked() RandomPinDialog randomPinDialog(6, mContext->getReaderName(), this); if (randomPinDialog.exec() == QDialog::Accepted && !randomPinDialog.getPin().isEmpty()) { - QToolButton* pinButton = qobject_cast(sender()); + auto* pinButton = qobject_cast(sender()); if (pinButton == nullptr) { qCCritical(gui) << "sender == nullptr"; diff --git a/src/ui/widget/step/StepAuthenticationEac1Widget.h b/src/ui/widget/step/StepAuthenticationEac1Widget.h index 2fe2bc2..ace4e34 100644 --- a/src/ui/widget/step/StepAuthenticationEac1Widget.h +++ b/src/ui/widget/step/StepAuthenticationEac1Widget.h @@ -46,8 +46,9 @@ class StepAuthenticationEac1Widget REDIRECTING_BROWSER, FINISHED, }; - Q_ENUM(State); + Q_ENUM(State) + public: StepAuthenticationEac1Widget(QWidget* pParent = nullptr); virtual ~StepAuthenticationEac1Widget() override; @@ -84,7 +85,7 @@ class StepAuthenticationEac1Widget void updateWidget(); void setupChatView(); void prepareChatsForGui(); - void updateProgressPanel(int pProgressValue = 0, const QString& pProgressText = QString()); + void updateProgressPanel(); void addChatRightToGui(AccessRight pRight, bool pOptional, int pListSize); void clearPinWidgetLayout(); void createBasicReaderWidget(); diff --git a/src/ui/widget/step/StepChooseCardGui.cpp b/src/ui/widget/step/StepChooseCardGui.cpp index 1f7c241..b28c664 100644 --- a/src/ui/widget/step/StepChooseCardGui.cpp +++ b/src/ui/widget/step/StepChooseCardGui.cpp @@ -7,6 +7,7 @@ #include "HelpAction.h" #include "ReaderConfiguration.h" #include "RemoteClient.h" +#include "states/StateUnfortunateCardPosition.h" #include "step/AuthenticateStepsWidget.h" #include @@ -29,6 +30,7 @@ StepChooseCardGui::StepChooseCardGui(const QSharedPointer& pCon , mReaderDeviceGui(new ReaderDeviceGui(pParent)) , mCancelButton(nullptr) , mDeviceButton(nullptr) + , mRetryButton(nullptr) , mSubDialogOpen(false) { const auto& widget = qobject_cast(pParent); @@ -43,8 +45,11 @@ StepChooseCardGui::StepChooseCardGui(const QSharedPointer& pCon mCancelButton = mInformationMessageBox->addButton(tr("Cancel"), QMessageBox::NoRole); mDeviceButton = mInformationMessageBox->addButton(tr("Settings"), QMessageBox::YesRole); mDeviceButton->setFocus(); + mRetryButton = mInformationMessageBox->addButton(tr("Retry connection"), QMessageBox::YesRole); + mRetryButton->hide(); connect(mReaderDeviceGui.data(), &ReaderDeviceGui::fireFinished, this, &StepChooseCardGui::onSubDialogFinished); + connect(mContext.data(), &WorkflowContext::fireCancelWorkflow, mInformationMessageBox.data(), &QMessageBox::reject); } @@ -101,16 +106,19 @@ QString StepChooseCardGui::formatErrorMessages(const QString& pMessage1, const Q } -void StepChooseCardGui::updateErrorMessage(const QString& pTitle, const QString& pMessage1, const QString& pMessage2, bool closeErrorMessage) +void StepChooseCardGui::updateErrorMessage(const QString& pTitle, const QString& pMessage1, const QString& pMessage2, bool pCloseErrorMessage, const QString& pIconPath) { - if (closeErrorMessage || mContext->getStatus().isError()) + if (pCloseErrorMessage || mContext->getStatus().isError()) { mReaderDeviceGui->deactivate(); mInformationMessageBox->done(QMessageBox::InvalidRole); + Q_EMIT fireDeactivated(); return; } QString iconPath = getCurrentReaderImage(Env::getSingleton()->getReaderInfos(ReaderFilter::UniqueReaderTypes)); + iconPath = pIconPath.isEmpty() ? iconPath : pIconPath; + if (iconPath.isEmpty()) { mInformationMessageBox->setIcon(QMessageBox::Information); @@ -135,9 +143,16 @@ void StepChooseCardGui::updateErrorMessage(const QString& pTitle, const QString& return; } - mSubDialogOpen = true; + if (mInformationMessageBox->clickedButton() == mRetryButton) + { + mContext->setStateApproved(true); + Q_EMIT fireDeactivated(); + return; + } + if (mInformationMessageBox->clickedButton() == mDeviceButton) { + mSubDialogOpen = true; mReaderDeviceGui->activate(); } } @@ -156,7 +171,7 @@ void StepChooseCardGui::onSubDialogFinished() const QString StepChooseCardGui::connectedRemoteReaderNames() const { - RemoteClient* remoteClient = Env::getSingleton(); + auto* remoteClient = Env::getSingleton(); const auto deviceInfos = remoteClient->getConnectedDeviceInfos(); QStringList deviceNames; for (const auto& info : deviceInfos) @@ -178,13 +193,16 @@ void StepChooseCardGui::onReaderManagerSignal() QVector remoteReaders; for (const auto& readerInfo : readers) { - if (!readerInfo.sufficientApduLength()) - { - readerWithInsufficientApduLength = true; - } if (readerInfo.hasEidCard()) { - readersWithNpa << readerInfo; + if (!readerInfo.sufficientApduLength()) + { + readerWithInsufficientApduLength = true; + } + else + { + readersWithNpa << readerInfo; + } } if (readerInfo.getPlugInType() == ReaderManagerPlugInType::REMOTE) { @@ -192,6 +210,10 @@ void StepChooseCardGui::onReaderManagerSignal() } } + const bool askForRetry = AbstractState::isState(mContext->getCurrentState()); + mRetryButton->setVisible(askForRetry); + mDeviceButton->setVisible(!askForRetry); + if (readers.size() == 0) { const QString onlineHelpUrl = HelpAction::getOnlineUrl(QStringLiteral("stepChooseCardGui")); @@ -206,6 +228,13 @@ void StepChooseCardGui::onReaderManagerSignal() return; } + if (askForRetry) + { + updateErrorMessage(tr("Retry?"), tr("Weak NFC signal. Please reposition your card."), + QString(), false, QStringLiteral(":/images/reader/default_card_position.png")); + return; + } + if (readersWithNpa.size() == 0) { if (readerWithInsufficientApduLength) diff --git a/src/ui/widget/step/StepChooseCardGui.h b/src/ui/widget/step/StepChooseCardGui.h index 4ae884f..4b52f4d 100644 --- a/src/ui/widget/step/StepChooseCardGui.h +++ b/src/ui/widget/step/StepChooseCardGui.h @@ -31,12 +31,12 @@ class StepChooseCardGui StepAuthenticationEac1Widget* mAuthWidget; QPointer mInformationMessageBox; QPointer mReaderDeviceGui; - QPushButton* mCancelButton, * mDeviceButton; + QPushButton* mCancelButton, * mDeviceButton, * mRetryButton; bool mSubDialogOpen; QString getCurrentReaderImage(const QVector& pReaderInfos); static QString formatErrorMessages(const QString& pMessage1, const QString& pMessage2); - void updateErrorMessage(const QString& pTitle, const QString& pMessage1, const QString& pMessage2 = QString(), bool closeErrorMessage = false); + void updateErrorMessage(const QString& pTitle, const QString& pMessage1, const QString& pMessage2 = QString(), bool pCloseErrorMessage = false, const QString& pIconPath = QString()); const QString connectedRemoteReaderNames() const; private Q_SLOTS: diff --git a/src/ui/widget/step/StepErrorGui.cpp b/src/ui/widget/step/StepErrorGui.cpp index 9769916..293ef35 100644 --- a/src/ui/widget/step/StepErrorGui.cpp +++ b/src/ui/widget/step/StepErrorGui.cpp @@ -5,7 +5,7 @@ #include "StepErrorGui.h" #include "AppQtMainWidget.h" -#include "BuildHelper.h" +#include "Email.h" #include "EnumHelper.h" #include "Env.h" #include "generic/GuiUtils.h" @@ -132,41 +132,3 @@ void StepErrorGui::closeActiveDialogs() mMessageBox->reject(); } } - - -QString StepErrorGui::generateMailBody(const GlobalStatus& pStatus) const -{ - const auto& logHandler = Env::getSingleton(); - QStringList mailBody(tr("Please describe the error that occurred.")); - - if (logHandler->useLogfile()) - { - mailBody << tr("You may want to attach the logfile which can be saved from the error dialog."); - } - - const QString newLine = QStringLiteral("\n"); - mailBody << newLine; - - const auto& systemInfo = BuildHelper::getInformationHeader(); - for (const auto& info : systemInfo) - { - QString first = info.first; - QString second = info.second; - - first.replace(QStringLiteral("&"), QStringLiteral("%26")); - second.replace(QStringLiteral("&"), QStringLiteral("%26")); - - mailBody << first + QStringLiteral(": ") + second; - } - - mailBody << newLine + tr("Error code") + QLatin1Char(':'); - mailBody << getEnumName(pStatus.getStatusCode()); - - if (logHandler->hasCriticalLog()) - { - const QString criticalMessages = QString::fromUtf8(logHandler->getCriticalLogWindow()); - mailBody << newLine + tr("Critical errors:") + newLine + criticalMessages; - } - - return mailBody.join(newLine); -} diff --git a/src/ui/widget/step/StepErrorGui.h b/src/ui/widget/step/StepErrorGui.h index d808927..b5a65bf 100644 --- a/src/ui/widget/step/StepErrorGui.h +++ b/src/ui/widget/step/StepErrorGui.h @@ -35,7 +35,6 @@ class StepErrorGui QSharedPointer mContext; AppQtMainWidget* const mMainWidget; QPointer mMessageBox; - QString generateMailBody(const GlobalStatus& pStatus) const; Q_SIGNALS: void switchedToPinSettings(); diff --git a/src/ui/widget/step/StepGui.h b/src/ui/widget/step/StepGui.h index 9fbd860..a7a85d4 100644 --- a/src/ui/widget/step/StepGui.h +++ b/src/ui/widget/step/StepGui.h @@ -76,7 +76,7 @@ class StepGui Q_SIGNALS: void fireUiFinished(); void fireCancelled(); - + void fireDeactivated(); }; } // namespace governikus diff --git a/src/ui/widget/workflow/GenericWorkflowGui.h b/src/ui/widget/workflow/GenericWorkflowGui.h index f5ca882..da05d22 100644 --- a/src/ui/widget/workflow/GenericWorkflowGui.h +++ b/src/ui/widget/workflow/GenericWorkflowGui.h @@ -63,6 +63,9 @@ class GenericWorkflowGui { QObject::connect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setForwardButtonState, mWidget, &WorkflowQtWidget::setForwardButtonState); QObject::connect(mStepGui->getStepGuiDelegate(), &StepGuiDelegate::setCancelButtonState, mWidget, &WorkflowQtWidget::setCancelButtonState); + QObject::connect(mStepGui.data(), &StepGui::fireDeactivated, this, [this](){ + deactivateCurrentStepUi(); + }); } pStepUi->activate(); } diff --git a/src/ui/widget/workflow/WorkflowAuthenticateQtGui.cpp b/src/ui/widget/workflow/WorkflowAuthenticateQtGui.cpp index 3c8215b..008cd39 100644 --- a/src/ui/widget/workflow/WorkflowAuthenticateQtGui.cpp +++ b/src/ui/widget/workflow/WorkflowAuthenticateQtGui.cpp @@ -16,6 +16,7 @@ #include "states/StateProcessing.h" #include "states/StateSelectReader.h" #include "states/StateTransmit.h" +#include "states/StateUnfortunateCardPosition.h" #include "states/StateWriteHistory.h" #include "step/AuthenticateStepsWidget.h" #include "step/StepAdviseUserToRemoveCardGui.h" @@ -164,6 +165,11 @@ void WorkflowAuthenticateQtGui::onStateChanged(const QString& pNewState) } } } + else if (AbstractState::isState(pNewState)) + { + approveNewState = false; + activateStepUi(mChooseCardGui); + } else if (AbstractState::isState(pNewState)) { activateStepUi(mDidAuthenticateGui); diff --git a/src/ui/widget/workflow/WorkflowChangePinQtGui.cpp b/src/ui/widget/workflow/WorkflowChangePinQtGui.cpp index 676f78f..b73106b 100644 --- a/src/ui/widget/workflow/WorkflowChangePinQtGui.cpp +++ b/src/ui/widget/workflow/WorkflowChangePinQtGui.cpp @@ -15,6 +15,7 @@ #include "states/StateEstablishPaceChannel.h" #include "states/StateMaintainCardConnection.h" #include "states/StateSelectReader.h" +#include "states/StateUnfortunateCardPosition.h" #include "step/StepChooseCardGui.h" #include "step/StepErrorGui.h" @@ -28,6 +29,7 @@ WorkflowChangePinQtGui::WorkflowChangePinQtGui(QSharedPointer { Q_ASSERT(mPinSettingsWidget); connect(mContext.data(), &WorkflowContext::fireStateChanged, this, &WorkflowChangePinQtGui::onStateChanged); + connect(mContext.data(), &ChangePinContext::firePaceResultUpdated, this, &WorkflowChangePinQtGui::onPaceResultUpdated); } @@ -57,7 +59,7 @@ bool WorkflowChangePinQtGui::verifyAbortWorkflow() } -void WorkflowChangePinQtGui::onStateChanged(const QString& pNextState) +void WorkflowChangePinQtGui::onStateChanged(const QString& pNewState) { if (mContext->getStatus().isError() && !mContext->isErrorReportedToUser()) { @@ -69,26 +71,29 @@ void WorkflowChangePinQtGui::onStateChanged(const QString& pNextState) mContext->setErrorReportedToUser(); } - if (AbstractState::isState(pNextState)) + if (AbstractState::isState(pNewState)) { activateStepUi(mChooseCardGui); } - else if (AbstractState::isState(pNextState)) + else if (AbstractState::isState(pNewState)) { GenericWorkflowGui::deactivate(); } - else if (AbstractState::isState(pNextState)) + else if (AbstractState::isState(pNewState)) { const bool isBasicReader = mContext->getCardConnection()->getReaderInfo().isBasicReader(); + bool stateAccepted; if (mContext->getEstablishPaceChannelType() == PacePasswordId::PACE_PUK) { - mPinSettingsWidget->updatePinButton(isBasicReader && mPinSettingsWidget->getPuk().isEmpty()); + stateAccepted = !(isBasicReader && mPinSettingsWidget->getPuk().isEmpty()); } else { - mPinSettingsWidget->updatePinButton(isBasicReader && mPinSettingsWidget->getPin().isEmpty()); + stateAccepted = !(isBasicReader && mPinSettingsWidget->getPin().isEmpty()); } + mPinSettingsWidget->updatePinButton(false); + if (CardReturnCodeUtil::equalsWrongPacePassword(mContext->getLastPaceResult())) { if (isBasicReader) @@ -103,12 +108,22 @@ void WorkflowChangePinQtGui::onStateChanged(const QString& pNextState) } } - if (mPinSettingsWidget->getPinButtonEnabled()) + if (!stateAccepted) { + if (isBasicReader) + { + mPinSettingsWidget->updatePasswordFields(); + mPinSettingsWidget->setPasswordFocus(); + } return; } } - else if (AbstractState::isState(pNextState)) + else if (AbstractState::isState(pNewState)) + { + activateStepUi(mChooseCardGui); + return; + } + else if (AbstractState::isState(pNewState)) { if (mContext->getCardConnection()->getReaderInfo().isBasicReader()) { @@ -120,7 +135,7 @@ void WorkflowChangePinQtGui::onStateChanged(const QString& pNextState) mContext->setPuk(mPinSettingsWidget->getPuk()); } } - else if (AbstractState::isState(pNextState)) + else if (AbstractState::isState(pNewState)) { if (mContext->getCardConnection()->getReaderInfo().isBasicReader()) { @@ -128,15 +143,18 @@ void WorkflowChangePinQtGui::onStateChanged(const QString& pNextState) mContext->setNewPin(mPinSettingsWidget->getNewPin()); } } - else if (AbstractState::isState(pNextState) && mContext->getStatus().isNoError()) + else if (AbstractState::isState(pNewState) && mContext->getStatus().isNoError()) { - bool pinBlocked = false; - if (mContext->getCardConnection()) - { - pinBlocked = (mContext->getCardConnection()->getReaderInfo().getRetryCounter() == 0); - } - mPinSettingsWidget->setMode(pinBlocked ? PinSettingsWidget::Mode::AfterPinUnblock : PinSettingsWidget::Mode::AfterPinChange); + mPinSettingsWidget->setMode(PinSettingsWidget::Mode::AfterPinChange); } - mContext->setStateApproved(); } + + +void WorkflowChangePinQtGui::onPaceResultUpdated() +{ + if (mContext->getLastPaceResult() == CardReturnCode::OK_PUK) + { + GuiUtils::showPinUnlockedDialog(mPinSettingsWidget); + } +} diff --git a/src/ui/widget/workflow/WorkflowChangePinQtGui.h b/src/ui/widget/workflow/WorkflowChangePinQtGui.h index edfa3c4..6d99b9e 100644 --- a/src/ui/widget/workflow/WorkflowChangePinQtGui.h +++ b/src/ui/widget/workflow/WorkflowChangePinQtGui.h @@ -31,7 +31,8 @@ class WorkflowChangePinQtGui virtual bool verifyAbortWorkflow() override; private Q_SLOTS: - void onStateChanged(const QString& pNextState); + void onStateChanged(const QString& pNewState); + void onPaceResultUpdated(); private: PinSettingsWidget* mPinSettingsWidget; diff --git a/src/ui/widget/workflow/WorkflowQtWidget.cpp b/src/ui/widget/workflow/WorkflowQtWidget.cpp index 124f875..64c3df8 100644 --- a/src/ui/widget/workflow/WorkflowQtWidget.cpp +++ b/src/ui/widget/workflow/WorkflowQtWidget.cpp @@ -20,11 +20,11 @@ WorkflowQtWidget::WorkflowQtWidget(QWidget* pParent) mMainLayout->addWidget(mStepWidgetArea); mMainLayout->setMargin(0); - QHBoxLayout* buttonLayout = new QHBoxLayout(); + auto* buttonLayout = new QHBoxLayout(); buttonLayout->setMargin(0); mMainLayout->addLayout(buttonLayout); - QSpacerItem* horizontalSpacer = new QSpacerItem(40, 1, QSizePolicy::Expanding, QSizePolicy::Ignored); + auto* horizontalSpacer = new QSpacerItem(40, 1, QSizePolicy::Expanding, QSizePolicy::Ignored); buttonLayout->addItem(horizontalSpacer); diff --git a/src/ui/widget/workflow/WorkflowSelfInfoQtGui.cpp b/src/ui/widget/workflow/WorkflowSelfInfoQtGui.cpp index 11e8606..0991e04 100644 --- a/src/ui/widget/workflow/WorkflowSelfInfoQtGui.cpp +++ b/src/ui/widget/workflow/WorkflowSelfInfoQtGui.cpp @@ -7,6 +7,7 @@ #include "AppSettings.h" #include "generic/GuiUtils.h" #include "states/FinalState.h" +#include "states/StateCheckRefreshAddress.h" #include "states/StateDidAuthenticateEac1.h" #include "states/StateDidAuthenticateEac2.h" #include "states/StateEditAccessRights.h" @@ -15,6 +16,7 @@ #include "states/StateMaintainCardConnection.h" #include "states/StateSelectReader.h" #include "states/StateTransmit.h" +#include "states/StateUnfortunateCardPosition.h" #include "states/StateWriteHistory.h" #include "step/AuthenticateStepsWidget.h" #include "step/StepAdviseUserToRemoveCardGui.h" @@ -149,6 +151,11 @@ void WorkflowSelfInfoQtGui::onStateChanged(const QString& pNewState) } } } + else if (AbstractState::isState(pNewState)) + { + approveNewState = false; + activateStepUi(mChooseCardGui); + } else if (AbstractState::isState(pNewState)) { activateStepUi(mDidAuthenticateGui); @@ -162,6 +169,10 @@ void WorkflowSelfInfoQtGui::onStateChanged(const QString& pNewState) { mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::READING_CARD_DATA); } + else if (AbstractState::isState(pNewState) && mDidAuthenticateGui->isActive()) + { + mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::REDIRECTING_BROWSER); + } else if (AbstractState::isState(pNewState) && mDidAuthenticateGui->isActive()) { mDidAuthenticateGui->setState(StepAuthenticationEac1Widget::State::FINISHED); diff --git a/src/whitelist_client/Survey.cpp b/src/whitelist_client/Survey.cpp deleted file mode 100644 index 9f9226a..0000000 --- a/src/whitelist_client/Survey.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/*! - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "Survey.h" - -#ifndef QT_NO_DEBUG -#include -#endif - -#include -#include - - -using namespace governikus; - - -#define VALUE_NAME(_name, _key)\ - inline QLatin1String _name(){\ - return QLatin1String(_key);\ - } - - -namespace -{ -VALUE_NAME(ROM, "Rom") -VALUE_NAME(BUILD_NUMBER, "BuildNumber") -VALUE_NAME(ANDROID_VERSION, "AndroidVersion") -VALUE_NAME(KERNEL_VERSION, "KernelVersion") -VALUE_NAME(MAXIMUM_NFC_PACKET_LENGTH, "MaximumNfcPacketLength") -VALUE_NAME(VENDOR, "Vendor") -VALUE_NAME(MODEL_NUMBER, "ModelNumber") -VALUE_NAME(MODEL_NAME, "ModelName") -VALUE_NAME(AUSWEISAPP_VERSION_NUMBER, "AusweisAppVersionNumber") -} // namespace - - -Survey::Survey(const QString& pBuildNumber, - const QString& pAndroidVersion, - const QString& pKernelVersion, - int pMaximumNfcPacketLength, - const QString& pVendor, - const QString& pModelNumber, - const QString& pModelName, - const QString& pAusweisAppVersionNumber) - : mNull(false) - , mBuildNumber(pBuildNumber) - , mAndroidVersion(pAndroidVersion) - , mKernelVersion(pKernelVersion) - , mMaximumNfcPacketLength(pMaximumNfcPacketLength) - , mVendor(pVendor) - , mModelNumber(pModelNumber) - , mModelName(pModelName) - , mAusweisAppVersionNumber(pAusweisAppVersionNumber) -{ -} - - -Survey::Survey() - : mNull(true) - , mBuildNumber() - , mAndroidVersion() - , mKernelVersion() - , mMaximumNfcPacketLength(0) - , mVendor() - , mModelNumber() - , mModelName() - , mAusweisAppVersionNumber() -{ -} - - -QByteArray Survey::toJsonByteArray() const -{ - QJsonObject rom; - rom[BUILD_NUMBER()] = mBuildNumber; - rom[ANDROID_VERSION()] = mAndroidVersion; - rom[KERNEL_VERSION()] = mKernelVersion; - rom[MAXIMUM_NFC_PACKET_LENGTH()] = QJsonValue(mMaximumNfcPacketLength); - - QJsonObject mainObject; - mainObject[ROM()] = rom; - mainObject[VENDOR()] = mVendor; - mainObject[MODEL_NUMBER()] = mModelNumber; - mainObject[MODEL_NAME()] = mModelName; - mainObject[AUSWEISAPP_VERSION_NUMBER()] = mAusweisAppVersionNumber; - -#ifndef QT_NO_DEBUG - if (QCoreApplication::applicationName().startsWith(QLatin1String("Test"))) - { - return QJsonDocument(mainObject).toJson(QJsonDocument::Indented); - } -#endif - - return QJsonDocument(mainObject).toJson(QJsonDocument::Compact); -} - - -bool Survey::isNull() const -{ - return mNull; -} diff --git a/src/whitelist_client/Survey.h b/src/whitelist_client/Survey.h deleted file mode 100644 index a550fd3..0000000 --- a/src/whitelist_client/Survey.h +++ /dev/null @@ -1,43 +0,0 @@ -/*! - * \brief Class holding information about an Android device to be sent to - * the whitelist server. - * - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include - -namespace governikus -{ -class Survey -{ - private: - const bool mNull; - const QString mBuildNumber; - const QString mAndroidVersion; - const QString mKernelVersion; - const int mMaximumNfcPacketLength; - const QString mVendor; - const QString mModelNumber; - const QString mModelName; - const QString mAusweisAppVersionNumber; - - public: - Survey(const QString& pBuildNumber, - const QString& pAndroidVersion, - const QString& pKernelVersion, - int pMaximumNfcPacketLength, - const QString& pVendor, - const QString& pModelNumber, - const QString& pModelName, - const QString& pAusweisAppVersionNumber); - - Survey(); - - QByteArray toJsonByteArray() const; - bool isNull() const; -}; - -} // namespace governikus diff --git a/src/whitelist_client/SurveyHandler.cpp b/src/whitelist_client/SurveyHandler.cpp deleted file mode 100644 index 7e2de16..0000000 --- a/src/whitelist_client/SurveyHandler.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/*! - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "SurveyHandler.h" - -#include "DeviceInfo.h" -#include "Env.h" -#include "NetworkManager.h" -#include "SecureStorage.h" - -#include -#include -#include -#include -#include - -using namespace governikus; - - -void SurveyHandler::transmitSurvey(const Survey& pSurvey) const -{ - const QUrl whitelistServerBaseUrl = SecureStorage::getInstance().getWhitelistServerBaseUrl(); - const QUrl postSurveyUrl(whitelistServerBaseUrl.toString() + QStringLiteral("/new")); - QNetworkRequest request(postSurveyUrl); - request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json; charset=UTF-8")); - - const QByteArray jsonData = pSurvey.toJsonByteArray(); - QNetworkReply* reply = Env::getSingleton()->post(request, jsonData); - QObject::connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); - - qDebug().noquote() << "Sent survey to whitelist server:" << jsonData; -} - - -SurveyHandler::SurveyHandler() -{ -} - - -void SurveyHandler::sendSurvey(int pMaximumNfcPackageLength) const -{ - const Survey survey = createSurvey(pMaximumNfcPackageLength); - - if (survey.isNull()) - { - qCritical() << "Cannot send survey containing no information at all"; - - return; - } - - transmitSurvey(survey); -} - - -Survey SurveyHandler::createSurvey(int pMaximumNfcPackageLength) const -{ - return Survey( - DeviceInfo::getOSBuildNumber(), - DeviceInfo::getOSVersion(), - DeviceInfo::getKernelVersion(), - pMaximumNfcPackageLength, - DeviceInfo::getVendor(), - DeviceInfo::getModelNumber(), - DeviceInfo::getModelName(), - QCoreApplication::applicationVersion()); -} diff --git a/src/whitelist_client/SurveyHandler.h b/src/whitelist_client/SurveyHandler.h deleted file mode 100644 index b3b747c..0000000 --- a/src/whitelist_client/SurveyHandler.h +++ /dev/null @@ -1,26 +0,0 @@ -/*! - * \brief Class holding information about an Android device to be sent to - * the whitelist server. - * - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "Survey.h" - -namespace governikus -{ -class SurveyHandler -{ - private: - Survey createSurvey(int pMaximumNfcPackageLength) const; - void transmitSurvey(const Survey& pSurvey) const; - - public: - SurveyHandler(); - - void sendSurvey(int pMaximumNfcPackageLength) const; -}; - -} // namespace governikus diff --git a/src/whitelist_client/SurveyModel.cpp b/src/whitelist_client/SurveyModel.cpp new file mode 100644 index 0000000..fd5264a --- /dev/null +++ b/src/whitelist_client/SurveyModel.cpp @@ -0,0 +1,160 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "SurveyModel.h" + +#include "DeviceInfo.h" +#include "Env.h" +#include "NetworkManager.h" +#include "SecureStorage.h" +#include "SingletonHelper.h" + +#include +#include +#include +#include +#include +#include + + +using namespace governikus; + +defineSingleton(SurveyModel) + +#define VALUE_NAME(_name, _key)\ + inline QLatin1String _name(){\ + return QLatin1String(_key);\ + } + + +namespace +{ +VALUE_NAME(ROM, "Rom") +VALUE_NAME(BUILD_NUMBER, "BuildNumber") +VALUE_NAME(ANDROID_VERSION, "AndroidVersion") +VALUE_NAME(KERNEL_VERSION, "KernelVersion") +VALUE_NAME(MAXIMUM_NFC_PACKET_LENGTH, "MaximumNfcPacketLength") +VALUE_NAME(VENDOR, "Vendor") +VALUE_NAME(MODEL_NUMBER, "ModelNumber") +VALUE_NAME(MODEL_NAME, "ModelName") +VALUE_NAME(AUSWEISAPP_VERSION_NUMBER, "AusweisAppVersionNumber") +} // namespace + + +SurveyModel::SurveyModel(QObject* pParent) + : QAbstractListModel(pParent) + , mBuildNumber(DeviceInfo::getOSBuildNumber()) + , mAndroidVersion(DeviceInfo::getOSVersion()) + , mKernelVersion(DeviceInfo::getKernelVersion()) + , mMaximumNfcPacketLength(0) + , mVendor(DeviceInfo::getVendor()) + , mModelNumber(DeviceInfo::getModelNumber()) + , mModelName(DeviceInfo::getModelName()) + , mAusweisAppVersionNumber(QCoreApplication::applicationVersion()) +{ + buildDataObject(); +} + + +SurveyModel& SurveyModel::getInstance() +{ + return *Instance; +} + + +int SurveyModel::rowCount(const QModelIndex&) const +{ + return mData.size(); +} + + +QVariant SurveyModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (pIndex.isValid() && pIndex.row() < rowCount()) + { + auto entry = mData[pIndex.row()]; + if (pRole == TITLE) + { + return entry.first; + } + if (pRole == VALUE) + { + return entry.second; + } + } + return QVariant(); +} + + +QHash SurveyModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(TITLE, "title"); + roles.insert(VALUE, "value"); + return roles; +} + + +void SurveyModel::buildDataObject() +{ + beginResetModel(); + mData.clear(); + mData += qMakePair(tr("Vendor"), mVendor); + mData += qMakePair(tr("Model Name"), mModelName); + mData += qMakePair(tr("Model Number"), mModelNumber); + mData += qMakePair(tr("Build Number"), mBuildNumber); + mData += qMakePair(tr("Android version"), mAndroidVersion); + mData += qMakePair(tr("Kernel version"), mKernelVersion); + mData += qMakePair(tr("Max. NFC Packet Length"), QString::number(mMaximumNfcPacketLength)); + mData += qMakePair(tr("AusweisApp2 Version"), mAusweisAppVersionNumber); + endResetModel(); +} + + +QByteArray SurveyModel::toJsonByteArray() const +{ + QJsonObject rom; + rom[BUILD_NUMBER()] = mBuildNumber; + rom[ANDROID_VERSION()] = mAndroidVersion; + rom[KERNEL_VERSION()] = mKernelVersion; + rom[MAXIMUM_NFC_PACKET_LENGTH()] = QJsonValue(mMaximumNfcPacketLength); + + QJsonObject mainObject; + mainObject[ROM()] = rom; + mainObject[VENDOR()] = mVendor; + mainObject[MODEL_NUMBER()] = mModelNumber; + mainObject[MODEL_NAME()] = mModelName; + mainObject[AUSWEISAPP_VERSION_NUMBER()] = mAusweisAppVersionNumber; + +#ifndef QT_NO_DEBUG + if (QCoreApplication::applicationName().startsWith(QLatin1String("Test"))) + { + return QJsonDocument(mainObject).toJson(QJsonDocument::Indented); + } +#endif + + return QJsonDocument(mainObject).toJson(QJsonDocument::Compact); +} + + +void SurveyModel::setMaximumNfcPacketLength(int pMaximumNfcPacketLength) +{ + mMaximumNfcPacketLength = pMaximumNfcPacketLength; + buildDataObject(); +} + + +void SurveyModel::transmitSurvey() const +{ + const QUrl whitelistServerBaseUrl = Env::getSingleton()->getWhitelistServerBaseUrl(); + const QUrl postSurveyUrl(whitelistServerBaseUrl.toString() + QStringLiteral("/new")); + QNetworkRequest request(postSurveyUrl); + request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json; charset=UTF-8")); + + const QByteArray jsonData = toJsonByteArray(); + QNetworkReply* reply = Env::getSingleton()->post(request, jsonData); + QObject::connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + + qDebug().noquote() << "Sent survey to whitelist server:" << jsonData; +} diff --git a/src/whitelist_client/SurveyModel.h b/src/whitelist_client/SurveyModel.h new file mode 100644 index 0000000..a998950 --- /dev/null +++ b/src/whitelist_client/SurveyModel.h @@ -0,0 +1,58 @@ +/*! + * \brief Class holding information about an Android device to be sent to + * the whitelist server. + * + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include +#include +#include + +namespace governikus +{ +class SurveyModel + : public QAbstractListModel +{ + Q_OBJECT + + private: + const QString mBuildNumber; + const QString mAndroidVersion; + const QString mKernelVersion; + int mMaximumNfcPacketLength; + const QString mVendor; + const QString mModelNumber; + const QString mModelName; + const QString mAusweisAppVersionNumber; + + QVector > mData; + + enum UserRoles + { + TITLE = Qt::UserRole + 1, + VALUE + }; + + + void buildDataObject(); + QByteArray toJsonByteArray() const; + + public: + explicit SurveyModel(QObject* pParent = nullptr); + + int rowCount(const QModelIndex& = QModelIndex()) const override; + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + QHash roleNames() const override; + + static SurveyModel& getInstance(); + + void setMaximumNfcPacketLength(int pMaximumNfcPacketLength); + + void transmitSurvey() const; +}; + +} // namespace governikus diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4ebf8cd..f721b25 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -36,7 +36,7 @@ FUNCTION(ADD_QML_TEST _sourcefile) EXTRACT_TESTNAME(TESTNAME ${_sourcefile}) GET_TEST_CMDLINE(CMD_PARAMS ${TESTNAME} SELECTORS ${_PARAM_SELECTORS}) - SET(CMD $ ${CMD_PARAMS} -input ${sourcefile} -import "${_import_path}") + SET(CMD $ ${CMD_PARAMS} -input ${sourcefile} -import "qrc:///qml/") SET(ENV_FAIL_ON_WARNING "QT_FATAL_WARNINGS=true") # Emasculate ASAN since QtQuick causes problems SET(ENV_EMASCULATE_ASAN "ASAN_OPTIONS=detect_leaks=0,new_delete_type_mismatch=0") @@ -53,7 +53,7 @@ FUNCTION(ADD_QML_TEST _sourcefile) ENDFUNCTION() -FUNCTION(ADD_QML_TEST_FILES _import_path) +FUNCTION(ADD_QML_TEST_FILES) cmake_parse_arguments(_PARAM "" "" "SELECTORS" ${ARGN}) IF(NOT BSD AND (NOT Qt5Core_VERSION VERSION_LESS 5.11.0 OR QT_VENDOR STREQUAL "Governikus")) SET(HAS_FILE_SELECTOR_PATCH TRUE) @@ -73,5 +73,7 @@ ENDFUNCTION() ADD_SUBDIRECTORY(helper) -ADD_SUBDIRECTORY(qml) +IF(TARGET Qt5::Qml) + ADD_SUBDIRECTORY(qml) +ENDIF() ADD_SUBDIRECTORY(qt) diff --git a/test/helper/CMakeLists.txt b/test/helper/CMakeLists.txt index 6e7ab9e..cf27986 100644 --- a/test/helper/CMakeLists.txt +++ b/test/helper/CMakeLists.txt @@ -1,7 +1,7 @@ ADD_PLATFORM_LIBRARY(AusweisAppTestHelper) TARGET_INCLUDE_DIRECTORIES(AusweisAppTestHelper SYSTEM PUBLIC ${PCSC_INCLUDE_DIRS}) -TARGET_LINK_LIBRARIES(AusweisAppTestHelper Qt5::Network Qt5::Test AusweisAppExternal::HttpParser AusweisAppActivation AusweisAppCard AusweisAppCardDrivers AusweisAppRemoteDevice AusweisAppNetwork AusweisAppCore AusweisAppUiWebsocket) +TARGET_LINK_LIBRARIES(AusweisAppTestHelper Qt5::Network Qt5::Test AusweisAppExternal::HttpParser AusweisAppActivation AusweisAppCard AusweisAppCardDrivers AusweisAppRemoteDevice AusweisAppNetwork AusweisAppCore AusweisAppUiWebsocket AusweisAppUiCommon) TARGET_COMPILE_DEFINITIONS(AusweisAppTestHelper PRIVATE QT_STATICPLUGIN) IF(DESKTOP) diff --git a/test/helper/CliHelper.cpp b/test/helper/CliHelper.cpp deleted file mode 100644 index 1136344..0000000 --- a/test/helper/CliHelper.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/*! - * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "CliHelper.h" - -#include - -#include - -using namespace governikus; - -CliHelper::CliHelper(QObject* pParent) - : QProcess(pParent) - , mTimer() - , mLoop() - , mOutput() - , mServerPort(0) -{ - qRegisterMetaType("QProcess::ProcessState"); - - mTimer.setSingleShot(true); - connect(&mTimer, &QTimer::timeout, &mLoop, &QEventLoop::quit); - connect(this, &QProcess::readyRead, this, &CliHelper::storeOutput); - - QString path = QCoreApplication::applicationDirPath() + QStringLiteral("/../../src/"); - QString app = path + QStringLiteral("AusweisApp2"); - QStringList args; - args << QStringLiteral("--ui") << QStringLiteral("cli"); - args << QStringLiteral("--port") << QStringLiteral("0"); - -#ifdef Q_OS_WIN - app += ".exe"; -#else - args << QStringLiteral("-platform") << QStringLiteral("offscreen"); -#endif - - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert(QStringLiteral("QT_LOGGING_TO_CONSOLE"), QStringLiteral("1")); - setProcessEnvironment(env); - setProgram(app); - setArguments(args); - setWorkingDirectory(path); - setReadChannel(QProcess::StandardError); -} - - -CliHelper::~CliHelper() -{ - disconnect(this, &QProcess::readyRead, this, &CliHelper::storeOutput); - tearDown(); -} - - -void CliHelper::storeOutput() -{ - while (canReadLine()) - { - mOutput += QString::fromUtf8(readLine()); - } - - if (!mOutput.isEmpty()) - { - mLoop.quit(); - } -} - - -void CliHelper::waitForOutput(const QString& pMsg, QRegularExpressionMatch* pMatcher, int pTimeout) -{ - QCOMPARE(state(), QProcess::Running); - - const QRegularExpression regex(pMsg); - mTimer.start(pTimeout); - while (mTimer.isActive() || !mOutput.isEmpty()) - { - if (mOutput.isEmpty()) - { - mLoop.exec(); - } - - while (!mOutput.isEmpty()) - { - const QString msg = mOutput.takeFirst(); - - auto matcher = regex.match(msg); - if (matcher.hasMatch()) - { - mTimer.stop(); - if (pMatcher) - { - *pMatcher = matcher; - } - return; - } - } - } - - QString notFound = QStringLiteral("Cannot find given output: ") + pMsg; - QFAIL(notFound.toUtf8().constData()); -} - - -void CliHelper::run() -{ - QCOMPARE(state(), QProcess::NotRunning); - mServerPort = 0; // reset if we use same object again - start(); - if (waitForStarted(TIMEOUT)) - { - waitForOutput(QStringLiteral("Try to load UI plugin: \"UIPlugInCli\"")); - } - - QCOMPARE(state(), QProcess::Running); -} - - -void CliHelper::stop() -{ - terminate(); - closeWriteChannel(); - waitForFinished(TIMEOUT); -} - - -void CliHelper::waitForPong() -{ - send("ping"); - waitForOutput(QStringLiteral("^stdinput .*: \"ping\"")); - waitForOutput(QStringLiteral("Pong!")); -} - - -quint16 CliHelper::getServerPort() -{ - if (mServerPort == 0) - { - send("port"); - QRegularExpressionMatch matcher; - waitForOutput(QStringLiteral("^cli .*: Port: ([0-9]{1,5})$"), &matcher); - - if (matcher.hasMatch() && !matcher.captured(1).isNull()) - { - bool converted = false; - auto port = matcher.captured(1).toUInt(&converted); - if (converted && port <= USHRT_MAX) - { - mServerPort = static_cast(port); - } - } - } - - return mServerPort; -} - - -void CliHelper::tearDown() -{ - if (state() != QProcess::NotRunning) - { - stop(); - - QCOMPARE(state(), QProcess::NotRunning); - QCOMPARE(exitCode(), 0); - if (state() != QProcess::NotRunning) - { - QProcess::close(); - QFAIL("kill application"); - } - } -} diff --git a/test/helper/CliHelper.h b/test/helper/CliHelper.h deleted file mode 100644 index d0483a6..0000000 --- a/test/helper/CliHelper.h +++ /dev/null @@ -1,58 +0,0 @@ -/*! - * \brief Helper to test app via CliPlugIn. - * - * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include -#include -#include -#include -#include - -#define CLI_VERIFY(cmd) cmd; QVERIFY(!QTest::currentTestFailed()) - -namespace governikus -{ - -class CliHelper - : public QProcess -{ - Q_OBJECT - - private: - QTimer mTimer; - QEventLoop mLoop; - QStringList mOutput; - quint16 mServerPort; - - private Q_SLOTS: - void storeOutput(); - - public: - static const int TIMEOUT = 15000; - CliHelper(QObject* pParent = nullptr); - virtual ~CliHelper(); - - void send(const char* pMsg) - { - write(pMsg); - write("\n"); - if (bytesToWrite()) - { - QVERIFY(waitForBytesWritten(TIMEOUT)); - } - } - - - void waitForOutput(const QString& pMsgList, QRegularExpressionMatch* pMatcher = nullptr, int pTimeout = TIMEOUT); - void waitForPong(); - void run(); - void stop(); - void tearDown(); - quint16 getServerPort(); -}; - -} // namespace governikus diff --git a/test/helper/MockActivationContext.h b/test/helper/MockActivationContext.h index a2b7a2d..c67fc9b 100644 --- a/test/helper/MockActivationContext.h +++ b/test/helper/MockActivationContext.h @@ -52,8 +52,8 @@ class MockActivationContext virtual bool sendErrorPage(http_status pStatusCode, const GlobalStatus& pStatus) override { - Q_UNUSED(pStatusCode); - Q_UNUSED(pStatus); + Q_UNUSED(pStatusCode) + Q_UNUSED(pStatus) mSendErroPageCalled = true; mSendError = mErrorMessageOnSend; return mErroPageValue; @@ -62,8 +62,8 @@ class MockActivationContext virtual bool sendRedirect(const QUrl& pRedirectAddress, const GlobalStatus& pStatus) override { - Q_UNUSED(pRedirectAddress); - Q_UNUSED(pStatus); + Q_UNUSED(pRedirectAddress) + Q_UNUSED(pStatus) mSendRedirectCalled = true; mSendError = mErrorMessageOnSend; return mRedirectValue; diff --git a/test/helper/MockCard.cpp b/test/helper/MockCard.cpp index 2299682..fcabfa0 100644 --- a/test/helper/MockCard.cpp +++ b/test/helper/MockCard.cpp @@ -34,7 +34,7 @@ CardReturnCode MockCard::disconnect() CardReturnCode MockCard::transmit(const CommandApdu& pCmd, ResponseApdu& pRes) { - Q_UNUSED(pCmd); + Q_UNUSED(pCmd) if (mCardConfig.mTransmits.isEmpty()) { qFatal("No (more) response APDU configured, but a(nother) command transmitted"); diff --git a/test/helper/MockCard.h b/test/helper/MockCard.h index 7f67b87..585bb8b 100644 --- a/test/helper/MockCard.h +++ b/test/helper/MockCard.h @@ -15,7 +15,7 @@ namespace governikus { -typedef QPair TransmitConfig; +using TransmitConfig = QPair; class MockCardConfig diff --git a/test/helper/MockCardConnectionWorker.cpp b/test/helper/MockCardConnectionWorker.cpp index bea5abb..870156e 100644 --- a/test/helper/MockCardConnectionWorker.cpp +++ b/test/helper/MockCardConnectionWorker.cpp @@ -41,7 +41,7 @@ void MockCardConnectionWorker::addPaceCode(CardReturnCode pCode) CardReturnCode MockCardConnectionWorker::transmit(const CommandApdu& pCommandApdu, ResponseApdu& pResponseApdu) { - Q_UNUSED(pCommandApdu); + Q_UNUSED(pCommandApdu) pResponseApdu.setBuffer(mResponseData.empty() ? QByteArray() : mResponseData.takeFirst()); return mResponseCodes.empty() ? CardReturnCode::UNDEFINED : mResponseCodes.takeFirst(); } @@ -57,17 +57,15 @@ CardReturnCode MockCardConnectionWorker::updateRetryCounter() } -CardReturnCode MockCardConnectionWorker::establishPaceChannel(PacePasswordId pPasswordId, +EstablishPaceChannelOutput MockCardConnectionWorker::establishPaceChannel(PacePasswordId pPasswordId, const QString& pPasswordValue, const QByteArray& pChat, - const QByteArray& pCertificateDescription, - EstablishPaceChannelOutput& pChannelOutput) + const QByteArray& pCertificateDescription) { - Q_UNUSED(pPasswordId); - Q_UNUSED(pPasswordValue); - Q_UNUSED(pChat); - Q_UNUSED(pCertificateDescription); - Q_UNUSED(pChannelOutput); + Q_UNUSED(pPasswordId) + Q_UNUSED(pPasswordValue) + Q_UNUSED(pChat) + Q_UNUSED(pCertificateDescription) return mPaceCodes.empty() ? CardReturnCode::UNDEFINED : mPaceCodes.takeFirst(); } @@ -80,8 +78,8 @@ CardReturnCode MockCardConnectionWorker::destroyPaceChannel() CardReturnCode MockCardConnectionWorker::setEidPin(const QString& pNewPin, quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) { - Q_UNUSED(pNewPin); - Q_UNUSED(pTimeoutSeconds); + Q_UNUSED(pNewPin) + Q_UNUSED(pTimeoutSeconds) pResponseApdu.setBuffer(mResponseData.empty() ? QByteArray() : mResponseData.takeFirst()); return mResponseCodes.empty() ? CardReturnCode::UNDEFINED : mResponseCodes.takeFirst(); } diff --git a/test/helper/MockCardConnectionWorker.h b/test/helper/MockCardConnectionWorker.h index a28c3e8..9a4f5e5 100644 --- a/test/helper/MockCardConnectionWorker.h +++ b/test/helper/MockCardConnectionWorker.h @@ -35,11 +35,10 @@ class MockCardConnectionWorker virtual CardReturnCode transmit(const CommandApdu& pCommandApdu, ResponseApdu& pResponseApdu) override; virtual CardReturnCode updateRetryCounter() override; - virtual CardReturnCode establishPaceChannel(PacePasswordId pPasswordId, + virtual EstablishPaceChannelOutput establishPaceChannel(PacePasswordId pPasswordId, const QString& pPasswordValue, const QByteArray& pChat, - const QByteArray& pCertificateDescription, - EstablishPaceChannelOutput& pChannelOutput) override; + const QByteArray& pCertificateDescription) override; virtual CardReturnCode destroyPaceChannel() override; virtual CardReturnCode setEidPin(const QString& pNewPin, quint8 pTimeoutSeconds, ResponseApdu& pResponseApdu) override; }; diff --git a/test/helper/MockDataChannel.cpp b/test/helper/MockDataChannel.cpp index 39e6113..c644445 100644 --- a/test/helper/MockDataChannel.cpp +++ b/test/helper/MockDataChannel.cpp @@ -24,7 +24,7 @@ MockDataChannel::~MockDataChannel() void MockDataChannel::close() { - Q_EMIT fireClosed(GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); + Q_EMIT fireClosed(GlobalStatus::Code::No_Error); } diff --git a/test/helper/MockDownloader.cpp b/test/helper/MockDownloader.cpp index f84df9e..fea8e7b 100644 --- a/test/helper/MockDownloader.cpp +++ b/test/helper/MockDownloader.cpp @@ -55,7 +55,7 @@ void MockDownloader::download(const QUrl& pUpdateUrl) void MockDownloader::downloadIfNew(const QUrl& pUpdateUrl, const QDateTime& pCurrentTimestamp) { - Q_UNUSED(pCurrentTimestamp); + Q_UNUSED(pCurrentTimestamp) download(pUpdateUrl); } diff --git a/test/helper/MockNetworkManager.cpp b/test/helper/MockNetworkManager.cpp index 859aca3..8e50a88 100644 --- a/test/helper/MockNetworkManager.cpp +++ b/test/helper/MockNetworkManager.cpp @@ -60,11 +60,22 @@ QNetworkReply* MockNetworkManager::get(QNetworkRequest& pRequest, const QByteArray& pSslSession, int pTimeoutInMilliSeconds) { - Q_UNUSED(pRequest); - Q_UNUSED(pSslSession); + Q_UNUSED(pRequest) + Q_UNUSED(pSslSession) + Q_UNUSED(pTimeoutInMilliSeconds) + + mLastRequest = &pRequest; + + return getReply(pRequest); +} + + +QNetworkReply* MockNetworkManager::post(QNetworkRequest& pRequest, const QByteArray& pData, int pTimeoutInMilliSeconds) +{ Q_UNUSED(pTimeoutInMilliSeconds); mLastRequest = &pRequest; + mLastData = QByteArray(pData); return getReply(pRequest); } @@ -77,12 +88,12 @@ QNetworkReply* MockNetworkManager::paos(QNetworkRequest& pRequest, const QByteArray& pSslSession, int pTimeoutInMilliSeconds) { - Q_UNUSED(pRequest); - Q_UNUSED(pNamespace); - Q_UNUSED(pData); - Q_UNUSED(pUsePsk); - Q_UNUSED(pSslSession); - Q_UNUSED(pTimeoutInMilliSeconds); + Q_UNUSED(pRequest) + Q_UNUSED(pNamespace) + Q_UNUSED(pData) + Q_UNUSED(pUsePsk) + Q_UNUSED(pSslSession) + Q_UNUSED(pTimeoutInMilliSeconds) return getReply(pRequest); } @@ -90,7 +101,7 @@ QNetworkReply* MockNetworkManager::paos(QNetworkRequest& pRequest, bool MockNetworkManager::checkUpdateServerCertificate(const QNetworkReply& pReply) { - Q_UNUSED(pReply); + Q_UNUSED(pReply) return true; } diff --git a/test/helper/MockNetworkManager.h b/test/helper/MockNetworkManager.h index dc47d08..e74fc1d 100644 --- a/test/helper/MockNetworkManager.h +++ b/test/helper/MockNetworkManager.h @@ -22,6 +22,7 @@ class MockNetworkManager MockNetworkReply* mNextReply; MockNetworkReply* mLastReply; QNetworkRequest* mLastRequest; + QByteArray mLastData; MockNetworkReply* getReply(const QNetworkRequest& pRequest); @@ -37,6 +38,10 @@ class MockNetworkManager virtual QNetworkReply* get(QNetworkRequest& pRequest, const QByteArray& pSslSession = QByteArray(), int pTimeoutInMilliSeconds = 30000) override; + virtual QNetworkReply* post(QNetworkRequest& pRequest, + const QByteArray& pData, + int pTimeoutInMilliSeconds = 30000) override; + virtual bool checkUpdateServerCertificate(const QNetworkReply& pReply) override; void setFilename(const QString& pFilename) @@ -65,6 +70,12 @@ class MockNetworkManager } + const QByteArray getLastData() const + { + return mLastData; + } + + Q_SIGNALS: void fireReply(); }; diff --git a/test/helper/MockNetworkReply.h b/test/helper/MockNetworkReply.h index bfc068b..429e8e3 100644 --- a/test/helper/MockNetworkReply.h +++ b/test/helper/MockNetworkReply.h @@ -12,6 +12,7 @@ #include class test_StateCheckRefreshAddress; +class test_StateGetSelfAuthenticationData; namespace governikus { @@ -23,6 +24,7 @@ class MockNetworkReply private: friend class ::test_StateCheckRefreshAddress; + friend class ::test_StateGetSelfAuthenticationData; MockSocket mSocket; public: @@ -30,6 +32,7 @@ class MockNetworkReply virtual ~MockNetworkReply() override; virtual void abort() override { + qDebug() << "Operation aborted"; } diff --git a/test/helper/MockReader.cpp b/test/helper/MockReader.cpp index a2ea2ea..75a9853 100644 --- a/test/helper/MockReader.cpp +++ b/test/helper/MockReader.cpp @@ -32,6 +32,7 @@ MockReader* MockReader::createMockReader(const QVector& pTransmi MockReader::MockReader(const QString& pReaderName) : Reader(ReaderManagerPlugInType::UNKNOWN, pReaderName) , mCard(nullptr) + , mEvent(CardEvent::NONE) { mReaderInfo.setConnected(true); mReaderInfo.setBasicReader(true); diff --git a/test/helper/MockReader.h b/test/helper/MockReader.h index 1599de1..3c1c15e 100644 --- a/test/helper/MockReader.h +++ b/test/helper/MockReader.h @@ -21,6 +21,7 @@ class MockReader Q_OBJECT QScopedPointer mCard; + CardEvent mEvent; public: static MockReader* createMockReader(const QVector& pTransmitConfig = QVector(), const QByteArray& pEfCardAccess = QByteArray()); @@ -58,10 +59,16 @@ class MockReader } + void setCardEvent(const CardEvent pEvent) + { + mEvent = pEvent; + } + + private: virtual Reader::CardEvent updateCard() override { - return Reader::CardEvent::NONE; + return mEvent; } diff --git a/test/helper/MockRemoteDispatcher.cpp b/test/helper/MockRemoteDispatcher.cpp index 3892f99..9d6ff45 100644 --- a/test/helper/MockRemoteDispatcher.cpp +++ b/test/helper/MockRemoteDispatcher.cpp @@ -101,7 +101,7 @@ void MockRemoteDispatcher::setState(DispatcherState pState) void MockRemoteDispatcher::onClosed() { - Q_EMIT fireClosed(GlobalStatus::Code::RemoteReader_CloseCode_NormalClose, mId); + Q_EMIT fireClosed(GlobalStatus::Code::No_Error, mId); } diff --git a/test/helper/MockRemoteServer.cpp b/test/helper/MockRemoteServer.cpp index f6ade75..93eff3c 100644 --- a/test/helper/MockRemoteServer.cpp +++ b/test/helper/MockRemoteServer.cpp @@ -24,7 +24,7 @@ bool MockRemoteServer::isRunning() const bool MockRemoteServer::start(const QString& pServerName) { - Q_UNUSED(pServerName); + Q_UNUSED(pServerName) mRunning = true; return true; } @@ -70,3 +70,9 @@ const QSharedPointer& MockRemoteServer::getMessageHandler( { return mServerMessageHandler; } + + +void MockRemoteServer::setMessageHandler(const QSharedPointer& pHandler) +{ + mServerMessageHandler = pHandler; +} diff --git a/test/helper/MockRemoteServer.h b/test/helper/MockRemoteServer.h index 7ec8464..a56b751 100644 --- a/test/helper/MockRemoteServer.h +++ b/test/helper/MockRemoteServer.h @@ -36,6 +36,7 @@ class MockRemoteServer void setConnected(bool pConnected); virtual QSslCertificate getCurrentCertificate() const override; virtual const QSharedPointer& getMessageHandler() const override; + void setMessageHandler(const QSharedPointer& pHandler); }; } // namespace governikus diff --git a/test/helper/MockSocket.cpp b/test/helper/MockSocket.cpp index 2258fbb..6e6d607 100644 --- a/test/helper/MockSocket.cpp +++ b/test/helper/MockSocket.cpp @@ -57,3 +57,10 @@ qint64 MockSocket::writeData(const char* pData, qint64 pMaxSize) mWriteBuffer += data; return data.size(); } + + +qint64 MockSocket::write(const QByteArray& pByteArray) +{ + mWriteBuffer += pByteArray; + return QIODevice::write(pByteArray); +} diff --git a/test/helper/MockSocket.h b/test/helper/MockSocket.h index 62bbcf0..e83665a 100644 --- a/test/helper/MockSocket.h +++ b/test/helper/MockSocket.h @@ -32,6 +32,7 @@ class MockSocket qint64 bytesAvailable() const override; qint64 readData(char* pDestination, qint64 pMaxSize) override; qint64 writeData(const char* pData, qint64 pMaxSize) override; + qint64 write(const QByteArray& pByteArray); }; } // namespace governikus diff --git a/test/helper/TestAuthContext.cpp b/test/helper/TestAuthContext.cpp index 3bcd127..0b26ebb 100644 --- a/test/helper/TestAuthContext.cpp +++ b/test/helper/TestAuthContext.cpp @@ -1,9 +1,11 @@ /*! * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany */ +#include "TestAuthContext.h" #include "paos/retrieve/DidAuthenticateEac1Parser.h" -#include "TestAuthContext.h" +#include "paos/retrieve/DidAuthenticateEac2Parser.h" + #include "TestFileHelper.h" @@ -13,11 +15,15 @@ using namespace governikus; TestAuthContext::TestAuthContext(ActivationContext* pActivationContext, const QString& pFileName) : AuthContext(QSharedPointer(pActivationContext)) , mDidAuthenticateEac1() + , mDidAuthenticateEac2() { mDidAuthenticateEac1.reset(static_cast(DidAuthenticateEac1Parser().parse(TestFileHelper::readFile(pFileName)))); setDidAuthenticateEac1(mDidAuthenticateEac1); setTerminalCvc(mDidAuthenticateEac1->getCvCertificates().at(0)); setDvCvc(mDidAuthenticateEac1->getCvCertificates().at(1)); + + mDidAuthenticateEac2.reset(dynamic_cast(DidAuthenticateEac2Parser().parse(TestFileHelper::readFile(":/paos/DIDAuthenticateEAC2.xml")))); + setDidAuthenticateEac2(mDidAuthenticateEac2); } @@ -64,3 +70,21 @@ void TestAuthContext::setOptionalAccessRights(const QSet& pAccessRi setDidAuthenticateEac1(mDidAuthenticateEac1); setTerminalCvc(mDidAuthenticateEac1->getCvCertificates().at(0)); } + + +void TestAuthContext::addCvCertificate(const QSharedPointer& pCvCertificate) +{ + mDidAuthenticateEac1->mEac1InputType.mCvCertificates += pCvCertificate; +} + + +void TestAuthContext::clearCvCertificates() +{ + mDidAuthenticateEac1->mEac1InputType.mCvCertificates.clear(); +} + + +void TestAuthContext::removeCvCertAt(int pPosition) +{ + mDidAuthenticateEac1->mEac1InputType.mCvCertificates.removeAt(pPosition); +} diff --git a/test/helper/TestAuthContext.h b/test/helper/TestAuthContext.h index 5e6ada6..2a68100 100644 --- a/test/helper/TestAuthContext.h +++ b/test/helper/TestAuthContext.h @@ -20,6 +20,7 @@ class TestAuthContext private: QSharedPointer mDidAuthenticateEac1; + QSharedPointer mDidAuthenticateEac2; public: TestAuthContext(ActivationContext* pActivationContext, const QString& pFileName); @@ -27,6 +28,9 @@ class TestAuthContext void setRequiredAccessRights(const QSet& pAccessRights); void setOptionalAccessRights(const QSet& pAccessRights); + void addCvCertificate(const QSharedPointer& pCvCertificate); + void clearCvCertificates(); + void removeCvCertAt(int pPosition); }; } // namespace governikus diff --git a/test/qml/QmlTestRunner.cpp b/test/qml/QmlTestRunner.cpp index 467942d..17a6d61 100644 --- a/test/qml/QmlTestRunner.cpp +++ b/test/qml/QmlTestRunner.cpp @@ -2,18 +2,23 @@ * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany */ +#include "LogHandler.h" #include "ResourceLoader.h" #include "UIPlugInQml.h" +#include #include #include using namespace governikus; +Q_DECLARE_LOGGING_CATEGORY(init) static void preRoutine() { + auto& logHandler = *Env::getSingleton(); + qCDebug(init) << "LogHandler initialized" << logHandler; ResourceLoader::getInstance().init(); UIPlugInQml::registerQmlTypes(); diff --git a/test/qml/desktop/CMakeLists.txt b/test/qml/desktop/CMakeLists.txt index 82152b4..2557ffc 100644 --- a/test/qml/desktop/CMakeLists.txt +++ b/test/qml/desktop/CMakeLists.txt @@ -1,2 +1,2 @@ -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "desktop;win") -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "desktop;mac") +ADD_QML_TEST_FILES(SELECTORS "desktop;win") +ADD_QML_TEST_FILES(SELECTORS "desktop;mac") diff --git a/test/qml/desktop/test_Global.qml b/test/qml/desktop/test_Global.qml index 7b85c78..448a457 100644 --- a/test/qml/desktop/test_Global.qml +++ b/test/qml/desktop/test_Global.qml @@ -1,13 +1,49 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { name: "ModuleImportTest" id: parent - function test_load_ContinueButton() { + function test_load_NavigationButton() { var item = createTemporaryQmlObject(" import Governikus.Global 1.0; - ContinueButton {} + NavigationButton { + buttonType: Qt.ForwardButton + } + ", parent); + item.destroy(); + } + + function test_load_ScrollablePane() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + ScrollablePane {} + ", parent); + item.destroy(); + } + + function test_load_TabbedPane() { + var item = createTemporaryQmlObject(" + import QtQuick 2.10 + import Governikus.Global 1.0 + import QtQml.Models 2.10; + TabbedPane { + id: pane + + anchors.fill: parent + anchors.margins: Constants.pane_padding + + sectionsModel: ['Item 1', 'Item 2', 'Item 3'] + contentObjectModel: ObjectModel { + Component { Rectangle { color: 'red' } } + Component { Rectangle { color: 'blue' } } + Component { Rectangle { color: 'black' } } + } + } ", parent); item.destroy(); } diff --git a/test/qml/generic/test_MainView.qml b/test/qml/desktop/test_MainView.qml similarity index 74% rename from test/qml/generic/test_MainView.qml rename to test/qml/desktop/test_MainView.qml index 0fe5782..07ce0f1 100644 --- a/test/qml/generic/test_MainView.qml +++ b/test/qml/desktop/test_MainView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/desktop/test_SettingsView.qml b/test/qml/desktop/test_SettingsView.qml new file mode 100644 index 0000000..f079ec6 --- /dev/null +++ b/test/qml/desktop/test_SettingsView.qml @@ -0,0 +1,26 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_SettingsView() { + var item = createTemporaryQmlObject(" + import Governikus.SettingsView 1.0; + SettingsView {} + ", parent); + item.destroy(); + } + + function test_load_ReaderView() { + var item = createTemporaryQmlObject(" + import Governikus.SettingsView 1.0; + TabbedReaderView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/desktop/test_TitleBar.qml b/test/qml/desktop/test_TitleBar.qml index e0b14bb..adecf39 100644 --- a/test/qml/desktop/test_TitleBar.qml +++ b/test/qml/desktop/test_TitleBar.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/desktop/test_TutorialView.qml b/test/qml/desktop/test_TutorialView.qml new file mode 100644 index 0000000..2f027da --- /dev/null +++ b/test/qml/desktop/test_TutorialView.qml @@ -0,0 +1,18 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_SetupAssistantView() { + var item = createTemporaryQmlObject(" + import Governikus.TutorialView 1.0; + SetupAssistantView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/desktop/test_View.qml b/test/qml/desktop/test_View.qml new file mode 100644 index 0000000..3c14ee8 --- /dev/null +++ b/test/qml/desktop/test_View.qml @@ -0,0 +1,34 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtTest 1.10 + +TestCase { + name: "ModuleLoadingView" + id: parent + + function test_load_Controller() { + var item = createTemporaryQmlObject(" + import Governikus.View 1.0; + Controller {} + ", parent); + item.destroy(); + } + + function test_load_FocusFrame() { + var item = createTemporaryQmlObject(" + import Governikus.View 1.0; + FocusFrame {} + ", parent); + item.destroy(); + } + + function test_load_FocusPoint() { + var item = createTemporaryQmlObject(" + import Governikus.View 1.0; + FocusPoint {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/CMakeLists.txt b/test/qml/generic/CMakeLists.txt index 1a779a3..23b8c9f 100644 --- a/test/qml/generic/CMakeLists.txt +++ b/test/qml/generic/CMakeLists.txt @@ -1,6 +1,6 @@ -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;phone;android") -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;tablet;android") -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;phone;ios") -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;tablet;ios") -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "desktop;win") -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "desktop;mac") +ADD_QML_TEST_FILES(SELECTORS "mobile;phone;android") +ADD_QML_TEST_FILES(SELECTORS "mobile;tablet;android") +ADD_QML_TEST_FILES(SELECTORS "mobile;phone;ios") +ADD_QML_TEST_FILES(SELECTORS "mobile;tablet;ios") +ADD_QML_TEST_FILES(SELECTORS "desktop;win") +ADD_QML_TEST_FILES(SELECTORS "desktop;mac") diff --git a/test/qml/mobile/test_ChangePinView.qml b/test/qml/generic/test_ChangePinView.qml similarity index 75% rename from test/qml/mobile/test_ChangePinView.qml rename to test/qml/generic/test_ChangePinView.qml index 37b8ba0..884967e 100644 --- a/test/qml/mobile/test_ChangePinView.qml +++ b/test/qml/generic/test_ChangePinView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/generic/test_EnterPasswordView.qml b/test/qml/generic/test_EnterPasswordView.qml new file mode 100644 index 0000000..bc58bee --- /dev/null +++ b/test/qml/generic/test_EnterPasswordView.qml @@ -0,0 +1,18 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_EnterPasswordView() { + var item = createTemporaryQmlObject(" + import Governikus.EnterPasswordView 1.0; + EnterPasswordView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_FeedbackView.qml b/test/qml/generic/test_FeedbackView.qml new file mode 100644 index 0000000..12c56c0 --- /dev/null +++ b/test/qml/generic/test_FeedbackView.qml @@ -0,0 +1,18 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_LogView() { + var item = createTemporaryQmlObject(" + import Governikus.FeedbackView 1.0; + LogView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_Global.qml b/test/qml/generic/test_Global.qml index 2989887..3e1db91 100644 --- a/test/qml/generic/test_Global.qml +++ b/test/qml/generic/test_Global.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 import Governikus.Global 1.0 @@ -19,9 +23,6 @@ TestCase { function test_Constants() { compare(Constants.black, "#000000") compare(Constants.white, "#ffffff") - - verify(Constants.titlebar_padding > 0) - verify(Constants.titlebar_spacing > 0) } function test_load_StatusIcon() { @@ -48,6 +49,14 @@ TestCase { item.destroy(); } + function test_load_GText() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GText {} + ", parent); + item.destroy(); + } + function test_load_PaneTitle() { var item = createTemporaryQmlObject(" import Governikus.Global 1.0; @@ -71,4 +80,48 @@ TestCase { ", parent); item.destroy(); } + + function test_load_GTextField() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GTextField {} + ", parent); + item.destroy(); + } + + function test_load_GComboBox() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GComboBox {} + ", parent); + item.destroy(); + } + + function test_load_ConfirmationPopup() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + ConfirmationPopup {} + ", parent); + item.destroy(); + } + + function test_load_SearchBar() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + SearchBar {} + ", parent); + item.destroy(); + } + + function test_load_GSeparator() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0 + + GSeparator { + width: parent.width + anchors.verticalCenter: parent.verticalCenter + } + ", parent); + item.destroy(); + } } diff --git a/test/qml/mobile/test_HistoryView.qml b/test/qml/generic/test_HistoryView.qml similarity index 75% rename from test/qml/mobile/test_HistoryView.qml rename to test/qml/generic/test_HistoryView.qml index e25cd6f..bcc77c1 100644 --- a/test/qml/mobile/test_HistoryView.qml +++ b/test/qml/generic/test_HistoryView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/generic/test_IdentifyView.qml b/test/qml/generic/test_IdentifyView.qml index f5a3ee2..e9e5b12 100644 --- a/test/qml/generic/test_IdentifyView.qml +++ b/test/qml/generic/test_IdentifyView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/generic/test_InformationView.qml b/test/qml/generic/test_InformationView.qml new file mode 100644 index 0000000..031e428 --- /dev/null +++ b/test/qml/generic/test_InformationView.qml @@ -0,0 +1,18 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_InformationView() { + var item = createTemporaryQmlObject(" + import Governikus.InformationView 1.0; + InformationView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_ProgressView.qml b/test/qml/generic/test_ProgressView.qml index 0a26824..cc50e1d 100644 --- a/test/qml/generic/test_ProgressView.qml +++ b/test/qml/generic/test_ProgressView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/generic/test_Provider.qml b/test/qml/generic/test_Provider.qml index d16a3a7..c2423a1 100644 --- a/test/qml/generic/test_Provider.qml +++ b/test/qml/generic/test_Provider.qml @@ -1,9 +1,21 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { name: "ModuleLoadingProvider" id: parent + function test_load_ProviderDetailView() { + var item = createTemporaryQmlObject(" + import Governikus.Provider 1.0; + ProviderDetailView {} + ", parent); + item.destroy(); + } + function test_load_ProviderInfoSection() { var item = createTemporaryQmlObject(" import Governikus.Provider 1.0; diff --git a/test/qml/mobile/test_ProviderView.qml b/test/qml/generic/test_ProviderView.qml similarity index 75% rename from test/qml/mobile/test_ProviderView.qml rename to test/qml/generic/test_ProviderView.qml index f79527d..f9f36f6 100644 --- a/test/qml/mobile/test_ProviderView.qml +++ b/test/qml/generic/test_ProviderView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/generic/test_ResultView.qml b/test/qml/generic/test_ResultView.qml index 0773c74..50bbc3f 100644 --- a/test/qml/generic/test_ResultView.qml +++ b/test/qml/generic/test_ResultView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/generic/test_SelfAuthenticationView.qml b/test/qml/generic/test_SelfAuthenticationView.qml new file mode 100644 index 0000000..9ba3ca9 --- /dev/null +++ b/test/qml/generic/test_SelfAuthenticationView.qml @@ -0,0 +1,18 @@ +/* + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +import QtTest 1.10 + +TestCase { + name: "ModuleImportTest" + id: parent + + function test_load_SelfAuthenticationView() { + var item = createTemporaryQmlObject(" + import Governikus.SelfAuthenticationView 1.0; + SelfAuthenticationView {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/generic/test_Style.qml b/test/qml/generic/test_Style.qml index 6aa0cd4..947f095 100644 --- a/test/qml/generic/test_Style.qml +++ b/test/qml/generic/test_Style.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { @@ -12,7 +16,7 @@ TestCase { item.destroy(); } - function test_load_ProviderStyle() { + function test_load_Style() { var item = createTemporaryQmlObject(" import QtQuick 2.0; import Governikus.Style 1.0; diff --git a/test/qml/generic/test_TitleBar.qml b/test/qml/generic/test_TitleBar.qml index 1e9d58b..7f6afb4 100644 --- a/test/qml/generic/test_TitleBar.qml +++ b/test/qml/generic/test_TitleBar.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/generic/test_UiPluginQml.qml b/test/qml/generic/test_UiPluginQml.qml index df6827b..4b67979 100644 --- a/test/qml/generic/test_UiPluginQml.qml +++ b/test/qml/generic/test_UiPluginQml.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 import Governikus.Type.UiModule 1.0 diff --git a/test/qml/generic/test_Utils.qml b/test/qml/generic/test_Utils.qml index 1f03fe4..d7ebc7e 100644 --- a/test/qml/generic/test_Utils.qml +++ b/test/qml/generic/test_Utils.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 import Governikus.Global 1.0 diff --git a/test/qml/generic/test_View.qml b/test/qml/generic/test_View.qml new file mode 100644 index 0000000..e016fa2 --- /dev/null +++ b/test/qml/generic/test_View.qml @@ -0,0 +1,18 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +import QtTest 1.10 + +TestCase { + name: "ModuleLoadingView" + id: parent + + function test_load_SectionPage() { + var item = createTemporaryQmlObject(" + import Governikus.View 1.0; + SectionPage {} + ", parent); + item.destroy(); + } +} diff --git a/test/qml/mobile/test_Workflow.qml b/test/qml/generic/test_Workflow.qml similarity index 83% rename from test/qml/mobile/test_Workflow.qml rename to test/qml/generic/test_Workflow.qml index a8b3d43..713fa24 100644 --- a/test/qml/mobile/test_Workflow.qml +++ b/test/qml/generic/test_Workflow.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/mobile/CMakeLists.txt b/test/qml/mobile/CMakeLists.txt index a1a4e5f..13abcb8 100644 --- a/test/qml/mobile/CMakeLists.txt +++ b/test/qml/mobile/CMakeLists.txt @@ -1,4 +1,4 @@ -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;phone;android") -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;tablet;android") -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;phone;ios") -ADD_QML_TEST_FILES(${RESOURCES_DIR}/qml SELECTORS "mobile;tablet;ios") +ADD_QML_TEST_FILES(SELECTORS "mobile;phone;android") +ADD_QML_TEST_FILES(SELECTORS "mobile;tablet;android") +ADD_QML_TEST_FILES(SELECTORS "mobile;phone;ios") +ADD_QML_TEST_FILES(SELECTORS "mobile;tablet;ios") diff --git a/test/qml/mobile/test_DeveloperView.qml b/test/qml/mobile/test_DeveloperView.qml index beb72af..61459ba 100644 --- a/test/qml/mobile/test_DeveloperView.qml +++ b/test/qml/mobile/test_DeveloperView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/mobile/test_EnterPinView.qml b/test/qml/mobile/test_EnterPinView.qml deleted file mode 100644 index fa1b9d3..0000000 --- a/test/qml/mobile/test_EnterPinView.qml +++ /dev/null @@ -1,14 +0,0 @@ -import QtTest 1.10 - -TestCase { - name: "ModuleImportTest" - id: parent - - function test_load_EnterPinView() { - var item = createTemporaryQmlObject(" - import Governikus.EnterPinView 1.0; - EnterPinView {} - ", parent); - item.destroy(); - } -} diff --git a/test/qml/mobile/test_FeedbackView.qml b/test/qml/mobile/test_FeedbackView.qml index ba5801f..366ae18 100644 --- a/test/qml/mobile/test_FeedbackView.qml +++ b/test/qml/mobile/test_FeedbackView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { @@ -12,10 +16,10 @@ TestCase { item.destroy(); } - function test_load_Log() { + function test_load_StoreFeedbackPopup() { var item = createTemporaryQmlObject(" import Governikus.FeedbackView 1.0; - Log {} + StoreFeedbackPopup {} ", parent); item.destroy(); } diff --git a/test/qml/mobile/test_Global.qml b/test/qml/mobile/test_Global.qml index ba2c5aa..4be7e7f 100644 --- a/test/qml/mobile/test_Global.qml +++ b/test/qml/mobile/test_Global.qml @@ -1,25 +1,13 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { name: "ModuleImportTest" id: parent - function test_load_GComboBox() { - var item = createTemporaryQmlObject(" - import Governikus.Global 1.0; - GComboBox {} - ", parent); - item.destroy(); - } - - function test_load_ConfirmationPopup() { - var item = createTemporaryQmlObject(" - import Governikus.Global 1.0; - ConfirmationPopup {} - ", parent); - item.destroy(); - } - function test_load_GRadioButton() { var item = createTemporaryQmlObject(" import Governikus.Global 1.0; @@ -36,14 +24,6 @@ TestCase { item.destroy(); } - function test_load_GTextField() { - var item = createTemporaryQmlObject(" - import Governikus.Global 1.0; - GTextField {} - ", parent); - item.destroy(); - } - function test_load_LocationButton() { var item = createTemporaryQmlObject(" import Governikus.Global 1.0; @@ -51,4 +31,44 @@ TestCase { ", parent); item.destroy(); } + + function test_load_ListItem() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + ListItem {} + ", parent); + item.destroy(); + } + + function test_load_GFlickable() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GFlickable {} + ", parent); + item.destroy(); + } + + function test_load_GListView() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GListView {} + ", parent); + item.destroy(); + } + + function test_load_GGridView() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GGridView {} + ", parent); + item.destroy(); + } + + function test_load_GScrollBar() { + var item = createTemporaryQmlObject(" + import Governikus.Global 1.0; + GScrollBar {} + ", parent); + item.destroy(); + } } diff --git a/test/qml/mobile/test_InformationView.qml b/test/qml/mobile/test_InformationView.qml index 5d8c40b..7b63ce4 100644 --- a/test/qml/mobile/test_InformationView.qml +++ b/test/qml/mobile/test_InformationView.qml @@ -1,17 +1,13 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { name: "ModuleLoadingInformationView" id: parent - function test_load_Information() { - var item = createTemporaryQmlObject(" - import Governikus.InformationView 1.0; - Information {} - ", parent); - item.destroy(); - } - function test_load_VersionInformationView() { var item = createTemporaryQmlObject(" import Governikus.InformationView 1.0; diff --git a/test/qml/mobile/test_MoreView.qml b/test/qml/mobile/test_MoreView.qml index 06e0aaa..b35c636 100644 --- a/test/qml/mobile/test_MoreView.qml +++ b/test/qml/mobile/test_MoreView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/mobile/test_Navigation.qml b/test/qml/mobile/test_Navigation.qml index 14a2743..6af6de8 100644 --- a/test/qml/mobile/test_Navigation.qml +++ b/test/qml/mobile/test_Navigation.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/mobile/test_Provider.qml b/test/qml/mobile/test_Provider.qml index 98f7bfd..7386aaf 100644 --- a/test/qml/mobile/test_Provider.qml +++ b/test/qml/mobile/test_Provider.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { @@ -12,14 +16,6 @@ TestCase { item.destroy(); } - function test_load_ProviderDetailView() { - var item = createTemporaryQmlObject(" - import Governikus.Provider 1.0; - ProviderDetailView {} - ", parent); - item.destroy(); - } - function test_load_ProviderHeader() { var item = createTemporaryQmlObject(" import Governikus.Provider 1.0; @@ -35,12 +31,4 @@ TestCase { ", parent); item.destroy(); } - - function test_load_ProviderViewDelegate() { - var item = createTemporaryQmlObject(" - import Governikus.Provider 1.0; - ProviderViewDelegate {} - ", parent); - item.destroy(); - } } diff --git a/test/qml/mobile/test_RemoteServiceView.qml b/test/qml/mobile/test_RemoteServiceView.qml index 05c6428..81b06fa 100644 --- a/test/qml/mobile/test_RemoteServiceView.qml +++ b/test/qml/mobile/test_RemoteServiceView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/mobile/test_SplashScreen.qml b/test/qml/mobile/test_SplashScreen.qml index 1e89376..ed89552 100644 --- a/test/qml/mobile/test_SplashScreen.qml +++ b/test/qml/mobile/test_SplashScreen.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/mobile/test_TechnologyInfo.qml b/test/qml/mobile/test_TechnologyInfo.qml index ec629f4..021f5c1 100644 --- a/test/qml/mobile/test_TechnologyInfo.qml +++ b/test/qml/mobile/test_TechnologyInfo.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2017-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/mobile/test_TutorialView.qml b/test/qml/mobile/test_TutorialView.qml index 46250a6..7fa7c3f 100644 --- a/test/qml/mobile/test_TutorialView.qml +++ b/test/qml/mobile/test_TutorialView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qml/mobile/test_View.qml b/test/qml/mobile/test_View.qml index d542e45..954e088 100644 --- a/test/qml/mobile/test_View.qml +++ b/test/qml/mobile/test_View.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { @@ -19,4 +23,12 @@ TestCase { ", parent); item.destroy(); } + + function test_load_ContentAreaLoader() { + var item = createTemporaryQmlObject(" + import Governikus.View 1.0; + ContentAreaLoader {} + ", parent); + item.destroy(); + } } diff --git a/test/qml/mobile/test_WhiteListClientView.qml b/test/qml/mobile/test_WhiteListClientView.qml index c0f07f1..effe55e 100644 --- a/test/qml/mobile/test_WhiteListClientView.qml +++ b/test/qml/mobile/test_WhiteListClientView.qml @@ -1,3 +1,7 @@ +/* + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + import QtTest 1.10 TestCase { diff --git a/test/qt/CMakeLists.txt b/test/qt/CMakeLists.txt index 40e2ab6..11ff329 100644 --- a/test/qt/CMakeLists.txt +++ b/test/qt/CMakeLists.txt @@ -1,3 +1,59 @@ +FUNCTION(GET_CAPITALIZED _out string) + STRING(SUBSTRING "${string}" 0 1 _tmp1) + STRING(SUBSTRING "${string}" 1 -1 _tmp2) + STRING(TOUPPER "${_tmp1}" _tmp1) + SET(${_out} ${_tmp1}${_tmp2} PARENT_SCOPE) +ENDFUNCTION() + + +FUNCTION(GET_SNAKE_TO_CAMEL_CASE _out string) + STRING(REPLACE "_" ";" string "${string}") + FOREACH(entry ${string}) + GET_CAPITALIZED(entry "${entry}") + SET(_tmp "${_tmp}${entry}") + ENDFOREACH() + + SET(${_out} "${_tmp}" PARENT_SCOPE) +ENDFUNCTION() + + +FUNCTION(EXTRACT_MODULES _out_module _out_submodule filename) + GET_FILENAME_COMPONENT(dir "${filename}" DIRECTORY) + + # make unit test directory to cmake list + STRING(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" dir "${dir}") + STRING(REPLACE "/" ";" dir "${dir}") + + # extract first subdirectory as module + LIST(GET dir 0 MODULE) + GET_SNAKE_TO_CAMEL_CASE(MODULE "${MODULE}") + + # extract second subdirectory as submodule if it exists + LIST(LENGTH dir dir_len) + IF(dir_len GREATER 1) + LIST(GET dir 1 SUBMODULE) + GET_CAPITALIZED(SUBMODULE "${SUBMODULE}") + ENDIF() + + # return values: module is required, submodule optional + SET(${_out_module} ${MODULE} PARENT_SCOPE) + SET(${_out_submodule} ${SUBMODULE} PARENT_SCOPE) +ENDFUNCTION() + + +FUNCTION(GET_MODULE _out filename) + EXTRACT_MODULES(MODULE SUBMODULE "${filename}") + + IF(TARGET "AusweisApp${MODULE}${SUBMODULE}") + SET(${_out} "AusweisApp${MODULE}${SUBMODULE}" PARENT_SCOPE) + ELSEIF(TARGET "AusweisApp${MODULE}") + SET(${_out} "AusweisApp${MODULE}" PARENT_SCOPE) + ELSE() + MESSAGE(FATAL_ERROR "Cannot detect module: ${filename}") + ENDIF() +ENDFUNCTION() + + FUNCTION(ADD_TEST_EXECUTABLE testname) IF(ANDROID) ADD_LIBRARY(${testname} SHARED ${ARGN}) @@ -5,57 +61,44 @@ FUNCTION(ADD_TEST_EXECUTABLE testname) ADD_EXECUTABLE(${testname} ${ARGN}) ENDIF() - TARGET_LINK_LIBRARIES(${testname} Qt5::Network Qt5::Test OpenSSL::Crypto) - TARGET_LINK_LIBRARIES(${testname} AusweisAppTestHelper AusweisAppCore AusweisAppCard AusweisAppGlobal AusweisAppCardDrivers AusweisAppServices AusweisAppSettings AusweisAppNetwork) - TARGET_LINK_LIBRARIES(${testname} AusweisAppActivationInternal AusweisAppUiJsonApi AusweisAppUiAidl AusweisAppUiQml) - TARGET_LINK_LIBRARIES(${testname} AusweisAppRemoteDevice AusweisAppExport) - TARGET_LINK_LIBRARIES(${testname} AusweisAppSecureStorage AusweisAppConfiguration AusweisAppFileProvider) - TARGET_LINK_LIBRARIES(${testname} QRC_FIXTURE_OBJ) + GET_MODULE(MODULE "${ARGN}") + TARGET_LINK_LIBRARIES(${testname} Qt5::Test AusweisAppTestHelper QRC_FIXTURE_OBJ ${MODULE}) +ENDFUNCTION() - IF(DESKTOP) - TARGET_LINK_LIBRARIES(${testname} Qt5::Widgets AusweisAppUiWidget AusweisAppCardPcsc AusweisAppActivationWebservice) - ENDIF() - IF(LINUX OR ANDROID OR IOS) - TARGET_LINK_LIBRARIES(${testname} AusweisAppCardBluetooth) - ENDIF() +FUNCTION(SHOULD_SKIP_TEST _out filename) + SET(${_out} FALSE PARENT_SCOPE) + STRING(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" test "${sourcefile}") - TARGET_LINK_LIBRARIES(${testname} AusweisAppWhitelistClient) + IF(IOS OR ANDROID) + IF(ANDROID_BUILD_AAR AND test MATCHES "ui/qml") + SET(${_out} TRUE PARENT_SCOPE) + RETURN() + ENDIF() - IF(WIN32) - TARGET_LINK_LIBRARIES(${testname} ${WIN_DEFAULT_LIBS}) - ELSEIF(MAC) - TARGET_LINK_LIBRARIES(${testname} ${OSX_APPKIT}) - ELSEIF(ANDROID) - TARGET_LINK_LIBRARIES(${testname} Qt5::AndroidExtras) + IF(test MATCHES "ui/widget" + OR test MATCHES "card/pcsc") + SET(${_out} TRUE PARENT_SCOPE) + RETURN() + ENDIF() ENDIF() ENDFUNCTION() FUNCTION(ADD_TEST_EXECUTABLE_SUBDIR) - SUBDIRLIST(SUBDIRS ${CMAKE_CURRENT_SOURCE_DIR}) - IF(IOS OR ANDROID) - LIST(REMOVE_ITEM SUBDIRS widget) - LIST(REMOVE_ITEM SUBDIRS cli) - LIST(REMOVE_ITEM SUBDIRS pcsc) - LIST(REMOVE_ITEM SUBDIRS drivers) - LIST(REMOVE_ITEM SUBDIRS activation_webservice) - ENDIF() + FILE(GLOB_RECURSE TEST_FILES "*.cpp") - FOREACH(SUBDIR ${SUBDIRS}) - FILE(GLOB_RECURSE TEST_SUBFILES "${SUBDIR}/*.cpp") + FOREACH(sourcefile ${TEST_FILES}) + SHOULD_SKIP_TEST(SKIP "${sourcefile}") + IF(SKIP) + CONTINUE() + ENDIF() - FOREACH(sourcefile ${TEST_SUBFILES}) - STRING(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "" sourcefile_relative "${sourcefile}") - IF(NOT (((APPLE OR WIN32 OR BSD) AND "${sourcefile_relative}" MATCHES "card/bluetooth") OR - ((IOS OR ANDROID) AND "${sourcefile_relative}" MATCHES "card/pcsc"))) - EXTRACT_TESTNAME(TESTNAME ${sourcefile}) - ADD_TEST_EXECUTABLE(${TESTNAME} ${sourcefile}) - GET_TEST_CMDLINE(TEST_CMDLINE ${TESTNAME}) - ADD_TEST(${TESTNAME} ${TESTNAME} ${TEST_CMDLINE}) - SET_TESTS_PROPERTIES(${TESTNAME} PROPERTIES LABELS "ausweisapp") - ENDIF() - ENDFOREACH() + EXTRACT_TESTNAME(TESTNAME ${sourcefile}) + ADD_TEST_EXECUTABLE(${TESTNAME} ${sourcefile}) + GET_TEST_CMDLINE(TEST_CMDLINE ${TESTNAME}) + ADD_TEST(${TESTNAME} ${TESTNAME} ${TEST_CMDLINE}) + SET_TESTS_PROPERTIES(${TESTNAME} PROPERTIES LABELS "ausweisapp") ENDFOREACH() ENDFUNCTION() diff --git a/test/qt/activation_webservice/test_Template.cpp b/test/qt/activation/webservice/test_Template.cpp similarity index 100% rename from test/qt/activation_webservice/test_Template.cpp rename to test/qt/activation/webservice/test_Template.cpp diff --git a/test/qt/activation_webservice/test_WebserviceActivationContext.cpp b/test/qt/activation/webservice/test_WebserviceActivationContext.cpp similarity index 77% rename from test/qt/activation_webservice/test_WebserviceActivationContext.cpp rename to test/qt/activation/webservice/test_WebserviceActivationContext.cpp index a0b7b02..8698ba7 100644 --- a/test/qt/activation_webservice/test_WebserviceActivationContext.cpp +++ b/test/qt/activation/webservice/test_WebserviceActivationContext.cpp @@ -32,23 +32,6 @@ class test_WebserviceActivationContext } - void test_SetCommonHeaders() - { - HttpResponse response; - WebserviceActivationContext context(mRequest); - context.setCommonHeaders(response); - - QVERIFY(response.getHeaders().keys().contains(QByteArrayLiteral("Connection"))); - QVERIFY(response.getHeaders().keys().contains(QByteArrayLiteral("Cache-Control"))); - QVERIFY(response.getHeaders().keys().contains(QByteArrayLiteral("Pragma"))); - - QMap headers = response.getHeaders(); - QCOMPARE(headers.take(QByteArrayLiteral("Connection")), QByteArrayLiteral("close")); - QCOMPARE(headers.take(QByteArrayLiteral("Cache-Control")), QByteArrayLiteral("no-cache, no-store")); - QCOMPARE(headers.take(QByteArrayLiteral("Pragma")), QByteArrayLiteral("no-cache")); - } - - void test_GetActivationUrl() { WebserviceActivationContext context(mRequest); @@ -96,6 +79,22 @@ class test_WebserviceActivationContext } + void test_SendErrorPage() + { + WebserviceActivationContext context(mRequest); + + QTest::ignoreMessage(QtDebugMsg, "Send error page to browser, error code 400"); + QVERIFY(context.sendErrorPage(HTTP_STATUS_BAD_REQUEST, GlobalStatus::Code::Unknown_Error)); + const auto buffer = mSocket->mWriteBuffer; + QVERIFY(buffer.contains("Connection: close")); + QVERIFY(buffer.contains("Cache-Control: no-cache, no-store")); + QVERIFY(buffer.contains("Pragma: no-cache")); + + mSocket->setSocketState(QAbstractSocket::UnconnectedState); + QVERIFY(!context.sendErrorPage(HTTP_STATUS_BAD_REQUEST, GlobalStatus::Code::Unknown_Error)); + } + + }; QTEST_GUILESS_MAIN(test_WebserviceActivationContext) diff --git a/test/qt/activation_webservice/test_WebserviceActivationHandler.cpp b/test/qt/activation/webservice/test_WebserviceActivationHandler.cpp similarity index 100% rename from test/qt/activation_webservice/test_WebserviceActivationHandler.cpp rename to test/qt/activation/webservice/test_WebserviceActivationHandler.cpp diff --git a/test/qt/card/asn1/test_AccessRoleAndRight.cpp b/test/qt/card/asn1/test_AccessRoleAndRight.cpp index 6ad2eeb..05f871f 100644 --- a/test/qt/card/asn1/test_AccessRoleAndRight.cpp +++ b/test/qt/card/asn1/test_AccessRoleAndRight.cpp @@ -67,7 +67,7 @@ class test_AccessRoleAndRight void checkFromTechnicalName() { QFETCH(AccessRight, value); - const AccessRight undefined = static_cast(UINT_MAX); + const auto undefined = static_cast(UINT_MAX); AccessRight right = undefined; const auto& func = [&](AccessRight pRight){ right = pRight; @@ -87,7 +87,7 @@ class test_AccessRoleAndRight void checkFromTechnicalNameInvalid() { - const AccessRight undefined = static_cast(UINT_MAX); + const auto undefined = static_cast(UINT_MAX); AccessRight right = undefined; const auto& func = [&](AccessRight pRight){ right = pRight; diff --git a/test/qt/card/asn1/test_CertificateDescription.cpp b/test/qt/card/asn1/test_CertificateDescription.cpp index c31b33e..d43ad18 100644 --- a/test/qt/card/asn1/test_CertificateDescription.cpp +++ b/test/qt/card/asn1/test_CertificateDescription.cpp @@ -320,9 +320,9 @@ class test_CertificateDescription } #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - certDescr->mCommCertificates = SKM_sk_new(ASN1_OCTET_STRING, 0); + certDescr->mCommCertificates = SKM_sk_new(ASN1_OCTET_STRING, nullptr); #else - certDescr->mCommCertificates = sk_ASN1_OCTET_STRING_new(0); + certDescr->mCommCertificates = sk_ASN1_OCTET_STRING_new(nullptr); #endif QByteArrayList commCertBytes; commCertBytes.append(QByteArray::fromHex("94B0AA7E8114F3E6DFCD52DA9F43E8B13CCB0589B8957E364728198FB4971AE6")); diff --git a/test/qt/card/asn1/test_EcdsaPublicKey.cpp b/test/qt/card/asn1/test_EcdsaPublicKey.cpp index 0b78fd8..c27c42c 100644 --- a/test/qt/card/asn1/test_EcdsaPublicKey.cpp +++ b/test/qt/card/asn1/test_EcdsaPublicKey.cpp @@ -210,7 +210,8 @@ class test_EcdsaPublicKey BN_clear_free(b); EC_GROUP_get_cofactor(ecGroup, cofactor, nullptr); - QCOMPARE(convert(cofactor).toHex().toUpper(), QByteArray("")); + const auto parsedCofactor = convert(cofactor).toHex().toUpper(); + QVERIFY(parsedCofactor == QByteArray("") || parsedCofactor == QByteArray("01")); // https://github.com/openssl/openssl/commit/a6186f39802f94937a46f7a41ef0c86b6334b592 BN_clear_free(cofactor); EC_GROUP_get_order(ecGroup, order, nullptr); diff --git a/test/qt/card/base/command/test_DidAuthenticateEAC2Command.cpp b/test/qt/card/base/command/test_DidAuthenticateEAC2Command.cpp new file mode 100644 index 0000000..8b15c2b --- /dev/null +++ b/test/qt/card/base/command/test_DidAuthenticateEAC2Command.cpp @@ -0,0 +1,91 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "command/DidAuthenticateEAC2Command.h" + +#include "asn1/CVCertificateChainBuilder.h" + +#include "MockCardConnectionWorker.h" +#include "TestFileHelper.h" + +#include + + +using namespace governikus; + + +class test_DidAuthenticateEAC2Command + : public QObject +{ + Q_OBJECT + QSharedPointer mWorker; + + static CVCertificateChain createCVCertificateChain(const QString& pFileName) + { + QByteArray data = TestFileHelper::readFile(QString(":/card/").append(pFileName)); + QSharedPointer certificate = CVCertificate::fromHex(data); + CVCertificateChainBuilder builder(false); + return builder.getChainStartingWith(certificate); + } + + + private Q_SLOTS: + void init() + { + mWorker.reset(new MockCardConnectionWorker()); + } + + + void cleanup() + { + mWorker.clear(); + } + + + void test_PerformTerminalAuthentication() + { + QByteArray input("0000"); + mWorker->addResponse(CardReturnCode::COMMAND_FAILED); + const CVCertificateChain cvcChain = createCVCertificateChain("cvca-DETESTeID00001.hex"); + DidAuthenticateEAC2Command command(mWorker, cvcChain, QString(), QString(), input); + + QByteArray bytes = QByteArray::fromHex("30 12" + " 06 0A 04007F00070202030202" + " 02 01 02" + " 02 01 08"); + + auto chipAuthenticationInfo = ChipAuthenticationInfo::decode(bytes); + QTest::ignoreMessage(QtDebugMsg, "Performing CA MSE:Set AT"); + QCOMPARE(command.performChipAuthentication(chipAuthenticationInfo, input, input, input), CardReturnCode::COMMAND_FAILED); + + mWorker->addResponse(CardReturnCode::OK); + QTest::ignoreMessage(QtDebugMsg, "Performing CA MSE:Set AT"); + QTest::ignoreMessage(QtWarningMsg, "CA MSE:Set AT failed: EMPTY"); + QCOMPARE(command.performChipAuthentication(chipAuthenticationInfo, input, input, input), CardReturnCode::PROTOCOL_ERROR); + + mWorker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + mWorker->addResponse(CardReturnCode::CANCELLATION_BY_USER); + QTest::ignoreMessage(QtDebugMsg, "Performing CA MSE:Set AT"); + QTest::ignoreMessage(QtDebugMsg, "Performing CA General Authenticate"); + QCOMPARE(command.performChipAuthentication(chipAuthenticationInfo, input, input, input), CardReturnCode::CANCELLATION_BY_USER); + + mWorker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + mWorker->addResponse(CardReturnCode::OK); + QTest::ignoreMessage(QtDebugMsg, "Performing CA MSE:Set AT"); + QTest::ignoreMessage(QtDebugMsg, "Performing CA General Authenticate"); + QTest::ignoreMessage(QtWarningMsg, "CA General Authenticate failed: EMPTY"); + QCOMPARE(command.performChipAuthentication(chipAuthenticationInfo, input, input, input), CardReturnCode::PROTOCOL_ERROR); + + mWorker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + mWorker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + QTest::ignoreMessage(QtDebugMsg, "Performing CA MSE:Set AT"); + QTest::ignoreMessage(QtDebugMsg, "Performing CA General Authenticate"); + QCOMPARE(command.performChipAuthentication(chipAuthenticationInfo, input, input, input), CardReturnCode::OK); + } + + +}; + +QTEST_GUILESS_MAIN(test_DidAuthenticateEAC2Command) +#include "test_DidAuthenticateEAC2Command.moc" diff --git a/test/qt/card/base/test_CardConnection.cpp b/test/qt/card/base/test_CardConnection.cpp index d710886..5b3a298 100644 --- a/test/qt/card/base/test_CardConnection.cpp +++ b/test/qt/card/base/test_CardConnection.cpp @@ -8,7 +8,7 @@ #include "MockCardConnectionWorker.h" -#include +#include using namespace governikus; diff --git a/test/qt/card/base/test_CardConnectionWorker.cpp b/test/qt/card/base/test_CardConnectionWorker.cpp new file mode 100644 index 0000000..f47aef2 --- /dev/null +++ b/test/qt/card/base/test_CardConnectionWorker.cpp @@ -0,0 +1,147 @@ +/*! + * \brief Tests for the class CardConnectionWorker. + * + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "CardConnectionWorker.h" + +#include "asn1/KnownOIDs.h" + +#include "MockReader.h" + +#include + +using namespace governikus; + +class test_CardConnectionWorker + : public QObject +{ + Q_OBJECT + QSharedPointer mWorker; + QScopedPointer mReader; + + void setCard() + { + QVector transmitConfigs; + transmitConfigs.append(TransmitConfig(CardReturnCode::OK, QByteArray::fromHex("6982"))); + transmitConfigs.append(TransmitConfig(CardReturnCode::OK, QByteArray::fromHex("9000"))); + MockCardConfig cardConfig(transmitConfigs); + mReader->setCard(cardConfig); + } + + + private Q_SLOTS: + void init() + { + mReader.reset(new MockReader()); + mWorker = CardConnectionWorker::create(mReader.data()); + } + + + void cleanup() + { + mWorker.clear(); + mReader.reset(); + } + + + void test_Transmit() + { + CommandApdu emptyCommandApdu(QByteArray("")); + ResponseApdu emptyApdu; + + //no card + QCOMPARE(mWorker->transmit(emptyCommandApdu, emptyApdu), CardReturnCode::CARD_NOT_FOUND); + + setCard(); + + //no secure messaging + QCOMPARE(mWorker->transmit(emptyCommandApdu, emptyApdu), CardReturnCode::OK); + } + + + void test_EstablishPaceChannel() + { + const QString password("111111"); + const QByteArray chat = QByteArray::fromHex("7F4C12060904007F00070301020253050000000F0F"); + const QByteArray certDescription = QByteArray::fromHex("30 8202A4" + " 06 0A 04007F00070301030103" + " A1 0E 0C0C442D547275737420476D6248" + " A3 3A 0C38476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E" + " A5 820248" + " 04 820244 4E616D652C20416E7363687269667420756E6420452D4D61696C2D4164726573736520646573204469656E737465616E626965746572733A0D0A476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E0D0A57696C68656C6D73747261C39F652034332F3433670D0A3130313137204265726C696E0D0A6265726C696E406764762E64650D0A0D0A4765736368C3A46674737A7765636B3A0D0A2D52656769737472696572756E6720756E64204C6F67696E20616D204744562D4D616B6C6572706F7274616C2D0D0A0D0A48696E7765697320617566206469652066C3BC722064656E204469656E737465616E626965746572207A757374C3A46E646967656E205374656C6C656E2C20646965206469652045696E68616C74756E672064657220566F7273636872696674656E207A756D20446174656E73636875747A206B6F6E74726F6C6C696572656E3A0D0A4265726C696E6572204265617566747261677465722066C3BC7220446174656E73636875747A20756E6420496E666F726D6174696F6E7366726569686569740D0A416E20646572205572616E696120342D31300D0A3130373837204265726C696E0D0A3033302F3133382038392D300D0A6D61696C626F7840646174656E73636875747A2D6265726C696E2E64650D0A687474703A2F2F7777772E646174656E73636875747A2D6265726C696E2E64650D0A416E737072656368706172746E65723A2044722E20416C6578616E64657220446978"); + + //no card + QCOMPARE(mWorker->establishPaceChannel(PacePasswordId::PACE_PIN, password, chat, certDescription).getPaceReturnCode(), CardReturnCode::CARD_NOT_FOUND); + + setCard(); + + //basic reader + QTest::ignoreMessage(QtInfoMsg, "Starting PACE for PACE_CAN"); + QTest::ignoreMessage(QtInfoMsg, "Finished PACE for PACE_CAN with result PROTOCOL_ERROR"); + QCOMPARE(mWorker->establishPaceChannel(PacePasswordId::PACE_CAN, password, chat, certDescription).getPaceReturnCode(), CardReturnCode::PROTOCOL_ERROR); + + //comfort reader + mReader->getReaderInfo().setBasicReader(false); + QTest::ignoreMessage(QtInfoMsg, "Starting PACE for PACE_PIN"); + QTest::ignoreMessage(QtInfoMsg, "Finished PACE for PACE_PIN with result COMMAND_FAILED"); + QTest::ignoreMessage(QtWarningMsg, "Establishment of PACE channel not supported"); + QCOMPARE(mWorker->establishPaceChannel(PacePasswordId::PACE_PIN, QString(), chat, certDescription).getPaceReturnCode(), CardReturnCode::COMMAND_FAILED); + } + + + void test_DestroyPaceChannel() + { + //no card + QCOMPARE(mWorker->destroyPaceChannel(), CardReturnCode::CARD_NOT_FOUND); + + setCard(); + + //basic reader + QTest::ignoreMessage(QtInfoMsg, "Destroying PACE channel"); + QTest::ignoreMessage(QtDebugMsg, "Destroying PACE channel with invalid command causing 6700 as return code"); + QCOMPARE(mWorker->destroyPaceChannel(), CardReturnCode::OK); + + //comfort reader + mReader->getReaderInfo().setBasicReader(false); + QTest::ignoreMessage(QtInfoMsg, "Destroying PACE channel"); + QTest::ignoreMessage(QtWarningMsg, "Destruction of PACE channel not supported"); + QCOMPARE(mWorker->destroyPaceChannel(), CardReturnCode::COMMAND_FAILED); + } + + + void test_SetEidPin() + { + const QString newPin("111111"); + ResponseApdu emptyApdu; + ResponseApdu apduSuccess(StatusCode::SUCCESS); + + //no card + QCOMPARE(mWorker->setEidPin(newPin, 5, emptyApdu), CardReturnCode::CARD_NOT_FOUND); + + setCard(); + + //basic reader + QTest::ignoreMessage(QtWarningMsg, "Modify PIN failed"); + QCOMPARE(mWorker->setEidPin(newPin, 5, emptyApdu), CardReturnCode::COMMAND_FAILED); + QCOMPARE(mWorker->setEidPin(newPin, 5, apduSuccess), CardReturnCode::OK); + + //comfort reader + mReader->getReaderInfo().setBasicReader(false); + QTest::ignoreMessage(QtWarningMsg, "Setting eID PIN is not supported"); + QCOMPARE(mWorker->setEidPin(QString(), 5, apduSuccess), CardReturnCode::COMMAND_FAILED); + } + + + void test_UpdateRetryCounter() + { + //no card + QCOMPARE(mWorker->updateRetryCounter(), CardReturnCode::CARD_NOT_FOUND); + } + + +}; + +QTEST_GUILESS_MAIN(test_CardConnectionWorker) +#include "test_CardConnectionWorker.moc" diff --git a/test/qt/card/base/test_CardInfo.cpp b/test/qt/card/base/test_CardInfo.cpp index 0071f1d..1d59b04 100644 --- a/test/qt/card/base/test_CardInfo.cpp +++ b/test/qt/card/base/test_CardInfo.cpp @@ -7,7 +7,7 @@ #include "CardInfo.h" -#include +#include using namespace governikus; @@ -39,19 +39,9 @@ class test_CardInfo } - void test_GetEidApplicationPath() - { - const CardInfo info1(CardType::NONE, QSharedPointer(), 3, false, false); - QCOMPARE(info1.getEidApplicationPath(), QString()); - - const CardInfo info2(CardType::EID_CARD, QSharedPointer(), 3, false, false); - QCOMPARE(info2.getEidApplicationPath(), QStringLiteral("e80704007f00070302")); - } - - void test_RetryCounterDeterminated() { - const CardInfo info1(CardType::EID_CARD, QSharedPointer(), CardInfo::UNDEFINED_RETRY_COUNTER, false, false); + const CardInfo info1(CardType::EID_CARD); QVERIFY(!info1.isRetryCounterDetermined()); const CardInfo info2(CardType::EID_CARD, QSharedPointer(), 3, false, false); diff --git a/test/qt/card/base/test_CommandApdu.cpp b/test/qt/card/base/test_CommandApdu.cpp index 5122a5a..1d8c8db 100644 --- a/test/qt/card/base/test_CommandApdu.cpp +++ b/test/qt/card/base/test_CommandApdu.cpp @@ -8,8 +8,8 @@ #include "LogHandler.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/card/base/test_Commands.cpp b/test/qt/card/base/test_Commands.cpp index 27d918c..92bdfb0 100644 --- a/test/qt/card/base/test_Commands.cpp +++ b/test/qt/card/base/test_Commands.cpp @@ -4,9 +4,11 @@ * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany */ -#include "Commands.h" +#include "EABuilder.h" +#include "GABuilder.h" +#include "PSOBuilder.h" -#include +#include using namespace governikus; diff --git a/test/qt/card/base/test_PinModify.cpp b/test/qt/card/base/test_PinModify.cpp index 55c1780..840fcf9 100644 --- a/test/qt/card/base/test_PinModify.cpp +++ b/test/qt/card/base/test_PinModify.cpp @@ -2,8 +2,8 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include "PinModify.h" diff --git a/test/qt/card/base/test_PinModifyOutput.cpp b/test/qt/card/base/test_PinModifyOutput.cpp index 708ed60..df5ddcf 100644 --- a/test/qt/card/base/test_PinModifyOutput.cpp +++ b/test/qt/card/base/test_PinModifyOutput.cpp @@ -2,8 +2,8 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include "PinModifyOutput.h" @@ -22,39 +22,39 @@ class test_PinModifyOutput { PinModifyOutput output; - output = PinModifyOutput(QByteArray::fromHex("02")); + output = PinModifyOutput(ResponseApdu(QByteArray::fromHex("02"))); QCOMPARE(output.getReturnCode(), CardReturnCode::UNKNOWN); QCOMPARE(output.getResponseApdu().getBuffer(), QByteArray::fromHex("02")); - output = PinModifyOutput(QByteArray::fromHex("6400")); + output = PinModifyOutput(ResponseApdu(QByteArray::fromHex("6400"))); QCOMPARE(output.getReturnCode(), CardReturnCode::INPUT_TIME_OUT); QCOMPARE(output.getResponseApdu().getBuffer(), QByteArray::fromHex("6400")); - output = PinModifyOutput(QByteArray::fromHex("6401")); + output = PinModifyOutput(ResponseApdu(QByteArray::fromHex("6401"))); QCOMPARE(output.getReturnCode(), CardReturnCode::CANCELLATION_BY_USER); QCOMPARE(output.getResponseApdu().getBuffer(), QByteArray::fromHex("6401")); - output = PinModifyOutput(QByteArray::fromHex("6402")); + output = PinModifyOutput(ResponseApdu(QByteArray::fromHex("6402"))); QCOMPARE(output.getReturnCode(), CardReturnCode::NEW_PIN_MISMATCH); QCOMPARE(output.getResponseApdu().getBuffer(), QByteArray::fromHex("6402")); - output = PinModifyOutput(QByteArray::fromHex("6403")); + output = PinModifyOutput(ResponseApdu(QByteArray::fromHex("6403"))); QCOMPARE(output.getReturnCode(), CardReturnCode::NEW_PIN_INVALID_LENGTH); QCOMPARE(output.getResponseApdu().getBuffer(), QByteArray::fromHex("6403")); - output = PinModifyOutput(QByteArray::fromHex("6a80")); + output = PinModifyOutput(ResponseApdu(QByteArray::fromHex("6a80"))); QCOMPARE(output.getReturnCode(), CardReturnCode::COMMAND_FAILED); QCOMPARE(output.getResponseApdu().getBuffer(), QByteArray::fromHex("6a80")); - output = PinModifyOutput(QByteArray::fromHex("6982")); + output = PinModifyOutput(ResponseApdu(QByteArray::fromHex("6982"))); QCOMPARE(output.getReturnCode(), CardReturnCode::UNKNOWN); QCOMPARE(output.getResponseApdu().getBuffer(), QByteArray::fromHex("6982")); - output = PinModifyOutput(QByteArray::fromHex("9000")); + output = PinModifyOutput(ResponseApdu(QByteArray::fromHex("9000"))); QCOMPARE(output.getReturnCode(), CardReturnCode::OK); QCOMPARE(output.getResponseApdu().getBuffer(), QByteArray::fromHex("9000")); - output = PinModifyOutput(QByteArray::fromHex("beef")); + output = PinModifyOutput(ResponseApdu(QByteArray::fromHex("beef"))); QCOMPARE(output.getReturnCode(), CardReturnCode::UNKNOWN); QCOMPARE(output.getResponseApdu().getBuffer(), QByteArray::fromHex("beef")); } @@ -62,7 +62,7 @@ class test_PinModifyOutput void toCcid() { - QCOMPARE(PinModifyOutput(QByteArray::fromHex("9000")).toCcid(), QByteArray::fromHex("9000")); + QCOMPARE(PinModifyOutput(ResponseApdu(QByteArray::fromHex("9000"))).toCcid(), QByteArray::fromHex("9000")); } diff --git a/test/qt/card/base/test_Reader.cpp b/test/qt/card/base/test_Reader.cpp index 9b0aa22..0e77425 100644 --- a/test/qt/card/base/test_Reader.cpp +++ b/test/qt/card/base/test_Reader.cpp @@ -8,6 +8,7 @@ #include "MockCardConnectionWorker.h" #include "MockReader.h" +#include "TestFileHelper.h" #include @@ -19,80 +20,106 @@ class test_Reader : public QObject { Q_OBJECT + QSharedPointer mReader; + QString mReaderName = QString("test reader"); + QSharedPointer mWorker; private Q_SLOTS: + void init() + { + mReader.reset(new MockReader(mReaderName)); + mWorker.reset(new MockCardConnectionWorker(mReader.data())); + } + + + void cleanup() + { + mReader.clear(); + mWorker.clear(); + } + + void test_CreateCardConnectionWorkerNoCard() { - const QString name = QStringLiteral("name"); - MockReader reader(name); - QTest::ignoreMessage(QtWarningMsg, "No card available"); - QCOMPARE(reader.createCardConnectionWorker(), QSharedPointer()); + QCOMPARE(mReader->createCardConnectionWorker(), QSharedPointer()); } void test_CreateCardConnectionWorkerConnected() { - const QString name = QStringLiteral("name"); - MockReader reader(name); - MockCard* card = reader.setCard(MockCardConfig(), QSharedPointer()); + MockCard* card = mReader->setCard(MockCardConfig(), QSharedPointer()); card->setConnected(true); QTest::ignoreMessage(QtWarningMsg, "Card is already connected"); - QCOMPARE(reader.createCardConnectionWorker(), QSharedPointer()); + QCOMPARE(mReader->createCardConnectionWorker(), QSharedPointer()); } void test_UpdateRetryCounterCommandFailed() { - const QString name = QStringLiteral("name"); - MockReader reader(name); CardInfo cInfo(CardType::UNKNOWN, QSharedPointer(), 3, false, false); - ReaderInfo rInfo(name, ReaderManagerPlugInType::UNKNOWN, cInfo); - reader.mReaderInfo = rInfo; - QSharedPointer worker(new MockCardConnectionWorker()); - - QCOMPARE(reader.updateRetryCounter(worker), CardReturnCode::COMMAND_FAILED); - } - - - void test_GetRetryCounterNoEfCardAccess() - { - const QString name = QStringLiteral("name"); - MockReader reader(name); - CardInfo cInfo(CardType::UNKNOWN, QSharedPointer(), 3, false, false); - ReaderInfo rInfo(name, ReaderManagerPlugInType::UNKNOWN, cInfo); - reader.mReaderInfo = rInfo; - QSharedPointer worker(new MockCardConnectionWorker()); + ReaderInfo rInfo(mReaderName, ReaderManagerPlugInType::UNKNOWN, cInfo); + mReader->setReaderInfo(rInfo); QTest::ignoreMessage(QtCriticalMsg, "Cannot get EF.CardAccess"); - int counter = 3; - bool deactivated = false; - QCOMPARE(reader.getRetryCounter(worker, counter, deactivated), CardReturnCode::COMMAND_FAILED); + QCOMPARE(mReader->updateRetryCounter(mWorker), CardReturnCode::COMMAND_FAILED); } - void test_FireUpdateSignal() + void test_UpdateRetryCounterUnknown() + { + QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/efCardAccess.hex")); + auto efCardAccess = EFCardAccess::decode(bytes); + CardInfo cInfo(CardType::UNKNOWN, efCardAccess, 3, false, false); + ReaderInfo rInfo(mReaderName, ReaderManagerPlugInType::UNKNOWN, cInfo); + mReader->setReaderInfo(rInfo); + mWorker->addResponse(CardReturnCode::UNKNOWN); + QCOMPARE(mReader->updateRetryCounter(mWorker), CardReturnCode::UNKNOWN); + } + + + void test_UpdateRetryCounter_OK_RetryCounterChanged() + { + QByteArray bytes = QByteArray::fromHex(TestFileHelper::readFile(":/card/efCardAccess.hex")); + auto efCardAccess = EFCardAccess::decode(bytes); + CardInfo cInfo(CardType::UNKNOWN, efCardAccess, 2, true, false); + ReaderInfo rInfo(mReaderName, ReaderManagerPlugInType::UNKNOWN, cInfo); + mReader->setReaderInfo(rInfo); + mWorker->addResponse(CardReturnCode::OK, QByteArray::fromHex("9000")); + QSignalSpy spy(mReader.data(), &Reader::fireCardRetryCounterChanged); + + QTest::ignoreMessage(QtDebugMsg, "StatusCode: SUCCESS"); + QTest::ignoreMessage(QtInfoMsg, "retrieved retry counter: 3 , was: 2 , PIN deactivated: false"); + QTest::ignoreMessage(QtDebugMsg, "fireCardRetryCounterChanged"); + QCOMPARE(mReader->updateRetryCounter(mWorker), CardReturnCode::OK); + QCOMPARE(mReader->getReaderInfo().getRetryCounter(), 3); + QVERIFY(!mReader->getReaderInfo().isPinDeactivated()); + QCOMPARE(spy.count(), 1); + } + + + void test_Update() { - const QString name = QStringLiteral("name"); - MockReader reader(name); CardInfo cInfo(CardType::UNKNOWN, QSharedPointer(), 3, false, false); - ReaderInfo rInfo(name, ReaderManagerPlugInType::UNKNOWN, cInfo); - reader.mReaderInfo = rInfo; + ReaderInfo rInfo(mReaderName, ReaderManagerPlugInType::UNKNOWN, cInfo); + mReader->setReaderInfo(rInfo); - QSignalSpy spyCardInserted(&reader, &Reader::fireCardInserted); - QSignalSpy spyCardRemoved(&reader, &Reader::fireCardRemoved); + QSignalSpy spyCardInserted(mReader.data(), &Reader::fireCardInserted); + QSignalSpy spyCardRemoved(mReader.data(), &Reader::fireCardRemoved); - reader.fireUpdateSignal(Reader::CardEvent::NONE); + mReader->update(); QCOMPARE(spyCardInserted.count(), 0); QCOMPARE(spyCardRemoved.count(), 0); + mReader->setCardEvent(Reader::CardEvent::CARD_INSERTED); QTest::ignoreMessage(QtInfoMsg, "Card inserted: {Type: UNKNOWN, Retry counter: 3, Pin deactivated: false}"); - reader.fireUpdateSignal(Reader::CardEvent::CARD_INSERTED); + mReader->update(); QCOMPARE(spyCardInserted.count(), 1); + mReader->setCardEvent(Reader::CardEvent::CARD_REMOVED); QTest::ignoreMessage(QtInfoMsg, "Card removed"); - reader.fireUpdateSignal(Reader::CardEvent::CARD_REMOVED); + mReader->update(); QCOMPARE(spyCardRemoved.count(), 1); } diff --git a/test/qt/card/bluetooth/test_BluetoothMessage.cpp b/test/qt/card/bluetooth/test_BluetoothMessage.cpp index c7c604c..692cb8b 100644 --- a/test/qt/card/bluetooth/test_BluetoothMessage.cpp +++ b/test/qt/card/bluetooth/test_BluetoothMessage.cpp @@ -4,9 +4,11 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ +#include "messages/BluetoothMessage.h" #include "messages/BluetoothMessageStatusInd.h" -#include -#include + +#include +#include using namespace governikus; @@ -33,6 +35,28 @@ class test_BluetoothMessage } + void toData() + { + BluetoothMessage msg(BluetoothMsgId::ConnectRequest); + const QSharedPointer maxMsgSize(new BluetoothMessageParameter(BluetoothParamId::MaxMsgSize, QByteArray::fromHex("111111"))); + const QSharedPointer connectionStatus(new BluetoothMessageParameter(BluetoothParamId::ConnectionStatus, QByteArray::fromHex("101010"))); + msg.addParameter(maxMsgSize); + msg.addParameter(connectionStatus); + QCOMPARE(msg.toData().toHex(), QByteArray("0002000000000003111111000100000310101000")); + } + + + void toString() + { + BluetoothMessage msg(BluetoothMsgId::ConnectRequest); + const QSharedPointer maxMsgSize(new BluetoothMessageParameter(BluetoothParamId::MaxMsgSize, QByteArray::fromHex("111111"))); + const QSharedPointer connectionStatus(new BluetoothMessageParameter(BluetoothParamId::ConnectionStatus, QByteArray::fromHex("101010"))); + msg.addParameter(maxMsgSize); + msg.addParameter(connectionStatus); + QCOMPARE(msg.toString(), QString("ConnectRequest | Parameter: MaxMsgSize | Value: 111111 | Parameter: ConnectionStatus | Value: 101010")); + } + + }; QTEST_GUILESS_MAIN(test_BluetoothMessage) diff --git a/test/qt/card/bluetooth/test_BluetoothMessageParameterCardReaderStatus.cpp b/test/qt/card/bluetooth/test_BluetoothMessageParameterCardReaderStatus.cpp index 79a0206..4b8b759 100644 --- a/test/qt/card/bluetooth/test_BluetoothMessageParameterCardReaderStatus.cpp +++ b/test/qt/card/bluetooth/test_BluetoothMessageParameterCardReaderStatus.cpp @@ -6,8 +6,8 @@ #include "messages/parameter/BluetoothMessageParameterCardReaderStatus.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/card/bluetooth/test_BluetoothMessageParameterMaxMsgSize.cpp b/test/qt/card/bluetooth/test_BluetoothMessageParameterMaxMsgSize.cpp index 2aa00a7..5f4d86e 100644 --- a/test/qt/card/bluetooth/test_BluetoothMessageParameterMaxMsgSize.cpp +++ b/test/qt/card/bluetooth/test_BluetoothMessageParameterMaxMsgSize.cpp @@ -4,8 +4,8 @@ */ #include "messages/parameter/BluetoothMessageParameterMaxMsgSize.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/card/bluetooth/test_BluetoothMessageParser.cpp b/test/qt/card/bluetooth/test_BluetoothMessageParser.cpp index 905479d..2ede1e5 100644 --- a/test/qt/card/bluetooth/test_BluetoothMessageParser.cpp +++ b/test/qt/card/bluetooth/test_BluetoothMessageParser.cpp @@ -8,8 +8,8 @@ #include "messages/BluetoothMessageStatusInd.h" #include "messages/parameter/BluetoothMessageParameterApduResponse.h" #include "messages/parameter/BluetoothMessageParameterStatusChange.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/drivers/test_ReaderDetector.cpp b/test/qt/card/drivers/test_ReaderDetector.cpp similarity index 100% rename from test/qt/drivers/test_ReaderDetector.cpp rename to test/qt/card/drivers/test_ReaderDetector.cpp diff --git a/test/qt/card/pace/test_EcUtil.cpp b/test/qt/card/pace/test_EcUtil.cpp index ca5a6c9..067973b 100644 --- a/test/qt/card/pace/test_EcUtil.cpp +++ b/test/qt/card/pace/test_EcUtil.cpp @@ -24,51 +24,44 @@ class test_EcUtil void createAndFreeEmptyCurve() { - QSharedPointer curve = EcUtil::create(static_cast(nullptr)); - curve.clear(); + EcUtil::create(static_cast(nullptr)); } void createAndFreeCurve() { - QSharedPointer curve = EllipticCurveFactory::create(8); - curve.clear(); + EllipticCurveFactory::create(8); } void createAndFreeEmptyKey() { - QSharedPointer key = EcUtil::create(static_cast(nullptr)); - key.clear(); + EcUtil::create(static_cast(nullptr)); } void createAndFreeKey() { - QSharedPointer key = EcUtil::create(EC_KEY_new()); - key.clear(); + EcUtil::create(EC_KEY_new()); } void createAndFreeEmptyPoint() { - QSharedPointer key = EcUtil::create(static_cast(nullptr)); - key.clear(); + EcUtil::create(static_cast(nullptr)); } void createAndFreePoint() { QSharedPointer curve = EllipticCurveFactory::create(8); - QSharedPointer key = EcUtil::create(EC_POINT_new(curve.data())); - key.clear(); + EcUtil::create(EC_POINT_new(curve.data())); } void createAndFreeEmptyBigNumber() { - QSharedPointer key = EcUtil::create(static_cast(nullptr)); - key.clear(); + EcUtil::create(static_cast(nullptr)); } @@ -76,8 +69,7 @@ class test_EcUtil { BIGNUM* bigNum = BN_new(); BN_bin2bn(reinterpret_cast("k37"), 3, bigNum); - QSharedPointer key = EcUtil::create(bigNum); - key.clear(); + EcUtil::create(bigNum); } diff --git a/test/qt/card/pace/test_KeyDerivationFunction.cpp b/test/qt/card/pace/test_KeyDerivationFunction.cpp index 5a388c8..02cb8aa 100644 --- a/test/qt/card/pace/test_KeyDerivationFunction.cpp +++ b/test/qt/card/pace/test_KeyDerivationFunction.cpp @@ -48,7 +48,7 @@ class test_KeyDerivationFunction { QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); - KeyDerivationFunction kdf = toByteArray(KnownOIDs::id_PACE::ECDH::GM_3DES_CBC_CBC); + KeyDerivationFunction kdf(toByteArray(KnownOIDs::id_PACE::ECDH::GM_3DES_CBC_CBC)); QCOMPARE(spyLog.count(), 1); QVERIFY(TestFileHelper::containsLog(spyLog, QLatin1String("3DES not supported"))); @@ -58,7 +58,7 @@ class test_KeyDerivationFunction void aes128Key() { - KeyDerivationFunction kdf = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_128); + KeyDerivationFunction kdf(toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_128)); QByteArray key = kdf.pi("123456"); @@ -69,7 +69,7 @@ class test_KeyDerivationFunction void aes196Key() { - KeyDerivationFunction kdf = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_192); + KeyDerivationFunction kdf(toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_192)); QByteArray key = kdf.pi("123456"); @@ -80,7 +80,7 @@ class test_KeyDerivationFunction void aes256Key() { - KeyDerivationFunction kdf = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_256); + KeyDerivationFunction kdf(toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_256)); QByteArray key = kdf.pi("123456"); diff --git a/test/qt/card/pace/test_PaceHandler.cpp b/test/qt/card/pace/test_PaceHandler.cpp index cab6dbc..1dc1010 100644 --- a/test/qt/card/pace/test_PaceHandler.cpp +++ b/test/qt/card/pace/test_PaceHandler.cpp @@ -6,6 +6,7 @@ #include "pace/PaceHandler.h" +#include "MockCardConnectionWorker.h" #include "MockReader.h" #include "TestFileHelper.h" @@ -212,6 +213,47 @@ class test_PaceHandler } + void establishPaceChannel_RetryAllowed() + { + QScopedPointer reader(MockReader::createMockReader(QVector(), mEfCardAccessBytes)); + QSharedPointer worker(new MockCardConnectionWorker(reader.data())); + QScopedPointer paceHandler(new PaceHandler(worker)); + + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); + + QCOMPARE(status, CardReturnCode::RETRY_ALLOWED); + } + + + void establishPaceChannel_KeyAgreementRetryAllowed() + { + QScopedPointer reader(MockReader::createMockReader(QVector(), mEfCardAccessBytes)); + QSharedPointer worker(new MockCardConnectionWorker(reader.data())); + QScopedPointer paceHandler(new PaceHandler(worker)); + + worker->addResponse(CardReturnCode::UNKNOWN, QByteArray::fromHex("6A80")); + + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); + + QCOMPARE(status, CardReturnCode::RETRY_ALLOWED); + } + + + void establishPaceChannel_KeyAgreementCommandFailed() + { + QScopedPointer reader(MockReader::createMockReader(QVector(), mEfCardAccessBytes)); + QSharedPointer worker(new MockCardConnectionWorker(reader.data())); + QScopedPointer paceHandler(new PaceHandler(worker)); + + worker->addResponse(CardReturnCode::UNKNOWN, QByteArray::fromHex("6A80")); + worker->addResponse(CardReturnCode::UNKNOWN, QByteArray::fromHex("9000")); + + CardReturnCode status = paceHandler->establishPaceChannel(PacePasswordId::PACE_PIN, "123456"); + + QCOMPARE(status, CardReturnCode::COMMAND_FAILED); + } + + // testcase TS_PACE_2.5.1c TR-03105 void failureOnMseSetAt() { @@ -227,6 +269,87 @@ class test_PaceHandler } + void transmitMSESetAT_ErrorMseSetAT_UNKNOWN() + { + QScopedPointer reader(MockReader::createMockReader(QVector(), mEfCardAccessBytes)); + QSharedPointer worker(new MockCardConnectionWorker(reader.data())); + QScopedPointer paceHandler(new PaceHandler(worker)); + + QTest::ignoreMessage(QtCriticalMsg, "Error on MSE:Set AT | UNKNOWN"); + worker->addResponse(CardReturnCode::UNKNOWN, QByteArray::fromHex("6A80")); + CardReturnCode status = paceHandler->transmitMSESetAT(PacePasswordId::PACE_PIN); + + QCOMPARE(status, CardReturnCode::UNKNOWN); + } + + + void transmitMSESetAT_OK() + { + QScopedPointer reader(MockReader::createMockReader(QVector(), mEfCardAccessBytes)); + QSharedPointer worker(new MockCardConnectionWorker(reader.data())); + QScopedPointer paceHandler(new PaceHandler(worker)); + QByteArray bytes = QByteArray::fromHex("30 0F" + " 06 0A 04007F00070202040202" + " 02 01 02"); + + auto paceInfo = PaceInfo::decode(bytes); + paceHandler->mPaceInfo = paceInfo; + QVERIFY(paceHandler->mPaceInfo != nullptr); + + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("009000")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("009000")); + CardReturnCode status = paceHandler->transmitMSESetAT(PacePasswordId::PACE_PIN); + + QCOMPARE(status, CardReturnCode::OK); + QCOMPARE(paceHandler->getStatusMseSetAt(), QByteArray::fromHex("0090")); + } + + + void transmitMSESetAT_ErrorMseSetAT_PROTOCOL_ERROR() + { + QScopedPointer reader(MockReader::createMockReader(QVector(), mEfCardAccessBytes)); + QSharedPointer worker(new MockCardConnectionWorker(reader.data())); + QScopedPointer paceHandler(new PaceHandler(worker)); + QByteArray bytes = QByteArray::fromHex("30 0F" + " 06 0A 04007F00070202040202" + " 02 01 02"); + + auto paceInfo = PaceInfo::decode(bytes); + paceHandler->mPaceInfo = paceInfo; + + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("0090")); + worker->addResponse(CardReturnCode::CANCELLATION_BY_USER, QByteArray::fromHex("0090")); + QTest::ignoreMessage(QtCriticalMsg, "Error on MSE:Set AT"); + QCOMPARE(paceHandler->transmitMSESetAT(PacePasswordId::PACE_PIN), CardReturnCode::PROTOCOL_ERROR); + QCOMPARE(paceHandler->getStatusMseSetAt(), QByteArray::fromHex("0090")); + + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("0090")); + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("006A80")); + QTest::ignoreMessage(QtCriticalMsg, "Error on MSE:Set AT"); + QCOMPARE(paceHandler->transmitMSESetAT(PacePasswordId::PACE_PIN), CardReturnCode::PROTOCOL_ERROR); + QCOMPARE(paceHandler->getStatusMseSetAt(), QByteArray::fromHex("006A")); + } + + + void transmitMSESetAT_ErrorMseSetAT_RETRY_ALLOWED() + { + QScopedPointer reader(MockReader::createMockReader(QVector(), mEfCardAccessBytes)); + QSharedPointer worker(new MockCardConnectionWorker(reader.data())); + QScopedPointer paceHandler(new PaceHandler(worker)); + QByteArray bytes = QByteArray::fromHex("30 0F" + " 06 0A 04007F00070202040202" + " 02 01 02"); + + auto paceInfo = PaceInfo::decode(bytes); + paceHandler->mPaceInfo = paceInfo; + + worker->addResponse(CardReturnCode::OK, QByteArray::fromHex("0090")); + worker->addResponse(CardReturnCode::UNDEFINED); + QTest::ignoreMessage(QtCriticalMsg, "Error on MSE:Set AT"); + QCOMPARE(paceHandler->transmitMSESetAT(PacePasswordId::PACE_PIN), CardReturnCode::RETRY_ALLOWED); + } + + }; QTEST_GUILESS_MAIN(test_PaceHandler) diff --git a/test/qt/card/pcsc/test_pcscReaderFeature.cpp b/test/qt/card/pcsc/test_pcscReaderFeature.cpp index 52475e5..5a2e7ce 100644 --- a/test/qt/card/pcsc/test_pcscReaderFeature.cpp +++ b/test/qt/card/pcsc/test_pcscReaderFeature.cpp @@ -9,8 +9,8 @@ #include "LogHandler.h" #include "TestFileHelper.h" -#include -#include +#include +#include using namespace governikus; @@ -25,7 +25,8 @@ class test_pcscReaderFeature int getFeatureCount(const PcscReaderFeature& pPcscReaderFeature) { int count = 0; - for (FeatureID feature : Enum::getList()) + const auto list = Enum::getList(); + for (FeatureID feature : list) { if (pPcscReaderFeature.contains(feature)) { diff --git a/test/qt/card/pcsc/test_pcscReaderPaceCapability.cpp b/test/qt/card/pcsc/test_pcscReaderPaceCapability.cpp index 2c3ffb8..e19500f 100644 --- a/test/qt/card/pcsc/test_pcscReaderPaceCapability.cpp +++ b/test/qt/card/pcsc/test_pcscReaderPaceCapability.cpp @@ -9,8 +9,8 @@ #include "LogHandler.h" #include "TestFileHelper.h" -#include -#include +#include +#include using namespace governikus; @@ -25,7 +25,8 @@ class test_pcscReaderPaceCapability int getCapabilityCount(const PcscReaderPaceCapability& pPcscReaderPaceCapability) { int count = 0; - for (PaceCapabilityId capability : Enum::getList()) + const auto list = Enum::getList(); + for (PaceCapabilityId capability : list) { if (pPcscReaderPaceCapability.contains(capability)) { diff --git a/test/qt/card/test_EstablishPaceChannel.cpp b/test/qt/card/test_EstablishPaceChannel.cpp index b7729fd..01d5433 100644 --- a/test/qt/card/test_EstablishPaceChannel.cpp +++ b/test/qt/card/test_EstablishPaceChannel.cpp @@ -4,8 +4,8 @@ * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include "EstablishPaceChannel.h" diff --git a/test/qt/card/test_EstablishPaceChannelOutput.cpp b/test/qt/card/test_EstablishPaceChannelOutput.cpp index 152d46a..b841fdf 100644 --- a/test/qt/card/test_EstablishPaceChannelOutput.cpp +++ b/test/qt/card/test_EstablishPaceChannelOutput.cpp @@ -4,8 +4,8 @@ * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include "EstablishPaceChannelOutput.h" #include "TestFileHelper.h" diff --git a/test/qt/card/test_GeneralAuthenticateResponse.cpp b/test/qt/card/test_GeneralAuthenticateResponse.cpp index a7da316..a8515a8 100644 --- a/test/qt/card/test_GeneralAuthenticateResponse.cpp +++ b/test/qt/card/test_GeneralAuthenticateResponse.cpp @@ -4,8 +4,8 @@ * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include "GeneralAuthenticateResponse.h" @@ -21,10 +21,9 @@ class test_GeneralAuthenticateResponse private Q_SLOTS: void parseGAEncryptedNonceResponse() { - QByteArray bytes = QByteArray::fromHex("7c1280105391ded7867c2d7df7f871ed6913c07d9000"); + const ResponseApdu apdu(QByteArray::fromHex("7c1280105391ded7867c2d7df7f871ed6913c07d9000")); - GAEncryptedNonceResponse response; - response.setBuffer(bytes); + const GAEncryptedNonceResponse response(apdu); QCOMPARE(response.getEncryptedNonce(), QByteArray::fromHex("5391ded7867c2d7df7f871ed6913c07d")); } @@ -32,10 +31,9 @@ class test_GeneralAuthenticateResponse void parseGAEncryptedNonceResponse_invalidData() { - QByteArray bytes = QByteArray::fromHex("7c1281105391ded7867c2d7df7f871ed6913c07d9000"); + const ResponseApdu apdu(QByteArray::fromHex("7c1281105391ded7867c2d7df7f871ed6913c07d9000")); - GAEncryptedNonceResponse response; - response.setBuffer(bytes); + const GAEncryptedNonceResponse response(apdu); QCOMPARE(response.getEncryptedNonce(), QByteArray()); } @@ -43,10 +41,9 @@ class test_GeneralAuthenticateResponse void parseGAMapNonceResponse() { - QByteArray bytes = QByteArray::fromHex("7c438241042a8199d469fde8f98e22bf8bb5a72804b5293bb54a8afa4d84e4b63217d163b61d78dc6453408bde19a86254ee3b0f03871964b71f1b57f77037ecdbedbe79b09000"); + const ResponseApdu apdu(QByteArray::fromHex("7c438241042a8199d469fde8f98e22bf8bb5a72804b5293bb54a8afa4d84e4b63217d163b61d78dc6453408bde19a86254ee3b0f03871964b71f1b57f77037ecdbedbe79b09000")); - GAMapNonceResponse response; - response.setBuffer(bytes); + const GAMapNonceResponse response(apdu); QCOMPARE(response.getMappingData(), QByteArray::fromHex("042a8199d469fde8f98e22bf8bb5a72804b5293bb54a8afa4d84e4b63217d163b61d78dc6453 408bde19a86254ee3b0f03871964b71f1b57f77037ecdbedbe79b0")); } @@ -54,10 +51,9 @@ class test_GeneralAuthenticateResponse void parseGAMapNonceResponse_invalidData() { - QByteArray bytes = QByteArray::fromHex("7c438141042a8199d469fde8f98e22bf8bb5a72804b5293bb54a8afa4d84e4b63217d163b61d78dc6453408bde19a86254ee3b0f03871964b71f1b57f77037ecdbedbe79b09000"); + const ResponseApdu apdu(QByteArray::fromHex("7c438141042a8199d469fde8f98e22bf8bb5a72804b5293bb54a8afa4d84e4b63217d163b61d78dc6453408bde19a86254ee3b0f03871964b71f1b57f77037ecdbedbe79b09000")); - GAMapNonceResponse response; - response.setBuffer(bytes); + const GAMapNonceResponse response(apdu); QCOMPARE(response.getMappingData(), QByteArray()); } @@ -65,10 +61,9 @@ class test_GeneralAuthenticateResponse void parseGAPerformKeyAgreementResponse() { - QByteArray bytes = QByteArray::fromHex("7c43844104a3be2ed0fccb4bf96df00be39a9c3e6b67d3a1118c95c195d0389fa14956c383a322c34f1b63a7bdb41f98b644aa9e15f823a2d726ef6ae8df3c10ac4e7298cc9000"); + const ResponseApdu apdu(QByteArray::fromHex("7c43844104a3be2ed0fccb4bf96df00be39a9c3e6b67d3a1118c95c195d0389fa14956c383a322c34f1b63a7bdb41f98b644aa9e15f823a2d726ef6ae8df3c10ac4e7298cc9000")); - GAPerformKeyAgreementResponse response; - response.setBuffer(bytes); + const GAPerformKeyAgreementResponse response(apdu); QCOMPARE(response.getEphemeralPublicKey(), QByteArray::fromHex("04a3be2ed0fccb4bf96df00be39a9c3e6b67d3a1118c95c195d0389fa14956c383a322c34f1b63a7bdb41f98b644aa9e15f823a2d726ef6ae8df3c10ac4e7298cc")); } @@ -76,10 +71,9 @@ class test_GeneralAuthenticateResponse void parseGAPerformKeyAgreementResponse_invalid() { - QByteArray bytes = QByteArray::fromHex("7c43814104a3be2ed0fccb4bf96df00be39a9c3e6b67d3a1118c95c195d0389fa14956c383a322c34f1b63a7bdb41f98b644aa9e15f823a2d726ef6ae8df3c10ac4e7298cc9000"); + const ResponseApdu apdu(QByteArray::fromHex("7c43814104a3be2ed0fccb4bf96df00be39a9c3e6b67d3a1118c95c195d0389fa14956c383a322c34f1b63a7bdb41f98b644aa9e15f823a2d726ef6ae8df3c10ac4e7298cc9000")); - GAPerformKeyAgreementResponse response; - response.setBuffer(bytes); + const GAPerformKeyAgreementResponse response(apdu); QCOMPARE(response.getEphemeralPublicKey(), QByteArray()); } @@ -87,10 +81,9 @@ class test_GeneralAuthenticateResponse void parseGAMutualAuthenticationResponse_withoutCARs() { - QByteArray bytes = QByteArray::fromHex("7c0a8608afcd013365384ba39000"); + const ResponseApdu apdu(QByteArray::fromHex("7c0a8608afcd013365384ba39000")); - GAMutualAuthenticationResponse response; - response.setBuffer(bytes); + const GAMutualAuthenticationResponse response(apdu); QCOMPARE(response.getAuthenticationToken(), QByteArray::fromHex("afcd013365384ba3")); QCOMPARE(response.getCarCurr(), QByteArray()); @@ -101,10 +94,9 @@ class test_GeneralAuthenticateResponse void testGAMutualAuthenticationResponse_oneCAR() { // this is the response from a new card that did not see any link certificates - QByteArray bytes = QByteArray::fromHex("7c1a860871204ff538eec464870e44454356434165494430303130339000"); + const ResponseApdu apdu(QByteArray::fromHex("7c1a860871204ff538eec464870e44454356434165494430303130339000")); - GAMutualAuthenticationResponse response; - response.setBuffer(bytes); + const GAMutualAuthenticationResponse response(apdu); QCOMPARE(response.getCarCurr(), QByteArray("DECVCAeID00103")); QCOMPARE(response.getCarPrev(), QByteArray()); @@ -114,10 +106,9 @@ class test_GeneralAuthenticateResponse void testGAMutualAuthenticationResponse_twoCARs() { // this is the response from a card that already saw link certificates - QByteArray bytes = QByteArray::fromHex("7c2a86086fa6266f2ef1f2d9870e4445544553546549443030303034880e44455445535465494430303030329000"); + const ResponseApdu apdu(QByteArray::fromHex("7c2a86086fa6266f2ef1f2d9870e4445544553546549443030303034880e44455445535465494430303030329000")); - GAMutualAuthenticationResponse response; - response.setBuffer(bytes); + const GAMutualAuthenticationResponse response(apdu); QCOMPARE(response.getCarCurr(), QByteArray("DETESTeID00004")); QCOMPARE(response.getCarPrev(), QByteArray("DETESTeID00002")); @@ -127,10 +118,9 @@ class test_GeneralAuthenticateResponse void testGAMutualAuthenticationResponse_invalid() { // this is the response from a new card that did not see any link certificates - QByteArray bytes = QByteArray::fromHex("7c1a810871204ff538eec464870e44454356434165494430303130339000"); + const ResponseApdu apdu(QByteArray::fromHex("7c1a810871204ff538eec464870e44454356434165494430303130339000")); - GAMutualAuthenticationResponse response; - response.setBuffer(bytes); + const GAMutualAuthenticationResponse response(apdu); QCOMPARE(response.getCarCurr(), QByteArray()); QCOMPARE(response.getCarPrev(), QByteArray()); @@ -139,10 +129,9 @@ class test_GeneralAuthenticateResponse void testGAChipAuthenticationResponse() { - QByteArray bytes = QByteArray::fromHex("7c1481085b5b32c5b15d012c8208aaa14cfba15994d39000"); + const ResponseApdu apdu(QByteArray::fromHex("7c1481085b5b32c5b15d012c8208aaa14cfba15994d39000")); - GAChipAuthenticationResponse response; - response.setBuffer(bytes); + const GAChipAuthenticationResponse response(apdu); QCOMPARE(response.getNonce(), QByteArray::fromHex("5b5b32c5b15d012c")); QCOMPARE(response.getAuthenticationToken(), QByteArray::fromHex("aaa14cfba15994d3")); @@ -151,10 +140,9 @@ class test_GeneralAuthenticateResponse void testGAChipAuthenticationResponse_invalid() { - QByteArray bytes = QByteArray::fromHex("7c148208aaa14cfba15994d39000"); + const ResponseApdu apdu(QByteArray::fromHex("7c148208aaa14cfba15994d39000")); - GAChipAuthenticationResponse response; - response.setBuffer(bytes); + const GAChipAuthenticationResponse response(apdu); QCOMPARE(response.getAuthenticationToken(), QByteArray()); QCOMPARE(response.getNonce(), QByteArray()); diff --git a/test/qt/card/test_MSEBuilder.cpp b/test/qt/card/test_MSEBuilder.cpp index 75574d9..63759eb 100644 --- a/test/qt/card/test_MSEBuilder.cpp +++ b/test/qt/card/test_MSEBuilder.cpp @@ -2,10 +2,10 @@ * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany */ -#include "Commands.h" +#include "MSEBuilder.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/card/test_SecureMessaging.cpp b/test/qt/card/test_SecureMessaging.cpp index 93ddbd5..d56d1c8 100644 --- a/test/qt/card/test_SecureMessaging.cpp +++ b/test/qt/card/test_SecureMessaging.cpp @@ -2,8 +2,8 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include "asn1/KnownOIDs.h" #include "pace/SecureMessaging.h" @@ -217,9 +217,8 @@ class test_SecureMessaging quint32 ssc = 0; auto result = encryptResponse(plainBuffer, ssc); ResponseApdu encryptedResponse(concat({result[0], result[1], result[2], result[3]})); - ResponseApdu decryptedResponse; - QVERIFY(mSecureMessaging->decrypt(encryptedResponse, decryptedResponse)); + ResponseApdu decryptedResponse = mSecureMessaging->decrypt(encryptedResponse); QCOMPARE(decryptedResponse.getBuffer(), plainBuffer); } @@ -231,9 +230,8 @@ class test_SecureMessaging quint32 ssc = 0; auto result = encryptResponse(plainBuffer, ssc); ResponseApdu encryptedResponse(concat({result[0], result[1], result[2], result[3]})); - ResponseApdu decryptedResponse; - QVERIFY(mSecureMessaging->decrypt(encryptedResponse, decryptedResponse)); + ResponseApdu decryptedResponse = mSecureMessaging->decrypt(encryptedResponse); QCOMPARE(decryptedResponse.getBuffer(), plainBuffer); } @@ -246,17 +244,16 @@ class test_SecureMessaging quint32 ssc = 0; auto result = encryptResponse(plainBuffer, ssc); ResponseApdu encryptedResponse(concat({result[0], result[1], result[2], result[3]})); - ResponseApdu decryptedResponse; QCOMPARE(ssc, 1u); - QVERIFY(mSecureMessaging->decrypt(encryptedResponse, decryptedResponse)); + ResponseApdu decryptedResponse = mSecureMessaging->decrypt(encryptedResponse); QCOMPARE(decryptedResponse.getBuffer(), plainBuffer); auto result2 = encryptResponse(plainBuffer2, ssc); ResponseApdu encryptedResponse2(concat({result2[0], result2[1], result2[2], result2[3]})); - ResponseApdu decryptedResponse2; + QCOMPARE(ssc, 2u); - QVERIFY(mSecureMessaging->decrypt(encryptedResponse2, decryptedResponse2)); + ResponseApdu decryptedResponse2 = mSecureMessaging->decrypt(encryptedResponse2); QCOMPARE(decryptedResponse2.getBuffer(), plainBuffer2); } @@ -268,9 +265,8 @@ class test_SecureMessaging quint32 ssc = 0; auto result = encryptResponse(plainBuffer, ssc); ResponseApdu encryptedResponse(concat({result[0], result[1], result[2], QByteArray::fromHex("6B00")})); - ResponseApdu decryptedResponse; - QVERIFY(!mSecureMessaging->decrypt(encryptedResponse, decryptedResponse)); + QVERIFY(mSecureMessaging->decrypt(encryptedResponse).isEmpty()); } @@ -281,9 +277,8 @@ class test_SecureMessaging quint32 ssc = 0; auto result = encryptResponse(plainBuffer, ssc); ResponseApdu encryptedResponse(concat({result[0], QByteArray::fromHex("99026B00"), result[2], result[3]})); - ResponseApdu decryptedResponse; - QVERIFY(!mSecureMessaging->decrypt(encryptedResponse, decryptedResponse)); + QVERIFY(mSecureMessaging->decrypt(encryptedResponse).isEmpty()); } @@ -294,9 +289,8 @@ class test_SecureMessaging quint32 ssc = 0; auto result = encryptResponse(plainBuffer, ssc); ResponseApdu encryptedResponse(concat({result[0], result[1], QByteArray::fromHex("8E080102030404030201"), result[3]})); - ResponseApdu decryptedResponse; - QVERIFY(!mSecureMessaging->decrypt(encryptedResponse, decryptedResponse)); + QVERIFY(mSecureMessaging->decrypt(encryptedResponse).isEmpty()); } diff --git a/test/qt/card/test_SecureMessagingResponse.cpp b/test/qt/card/test_SecureMessagingResponse.cpp index 50fb734..3503ad1 100644 --- a/test/qt/card/test_SecureMessagingResponse.cpp +++ b/test/qt/card/test_SecureMessagingResponse.cpp @@ -4,8 +4,8 @@ * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include "SecureMessagingResponse.h" diff --git a/test/qt/cli/test_UIPlugInCli.cpp b/test/qt/cli/test_UIPlugInCli.cpp deleted file mode 100644 index c3b1358..0000000 --- a/test/qt/cli/test_UIPlugInCli.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/*! - * \brief Unit tests for \ref UIPlugInCli - * - * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "CliHelper.h" - -#include - -using namespace governikus; - -class test_UIPlugInCli - : public QObject -{ - Q_OBJECT - - private: - QScopedPointer cli; - - private Q_SLOTS: - void initTestCase() - { - #ifdef Q_OS_WIN - QSKIP("Console is not supported at the moment"); - #endif - - #ifdef Q_OS_MACOS - QSKIP("QProcess/CliHelper is flaky on OSX"); - #endif - - #if defined(Q_OS_BSD4) || defined (Q_OS_LINUX) - QSKIP("Platform plugin seems broken"); - #endif - } - - - void init() - { - cli.reset(new CliHelper()); - } - - - void cleanup() - { - cli->tearDown(); - } - - - void quit() - { - CLI_VERIFY(cli->run()); - CLI_VERIFY(cli->waitForOutput("ready")); - CLI_VERIFY(cli->waitForPong()); - cli->send("help"); - CLI_VERIFY(cli->waitForOutput("Available commands:")); - cli->send("quit"); - CLI_VERIFY(cli->waitForOutput("Shutdown application")); - CLI_VERIFY(cli->waitForOutput("Emit fire shutdown")); - CLI_VERIFY(cli->waitForOutput("Shutdown ReaderManager")); - CLI_VERIFY(cli->waitForOutput("Unregister resource:")); - CLI_VERIFY(cli->waitForOutput("Quit event loop of QCoreApplication")); - cli->waitForFinished(); - QCOMPARE(cli->state(), QProcess::NotRunning); - QCOMPARE(cli->exitCode(), 0); - } - - - void term() - { - CLI_VERIFY(cli->run()); - CLI_VERIFY(cli->waitForOutput("ready")); - CLI_VERIFY(cli->waitForPong()); - cli->stop(); - QCOMPARE(cli->state(), QProcess::NotRunning); - QCOMPARE(cli->exitCode(), 0); - } - - - void termDuringOldPin() - { - CLI_VERIFY(cli->run()); - CLI_VERIFY(cli->waitForOutput("ready")); - cli->send("changepin"); - CLI_VERIFY(cli->waitForOutput("Please enter old PIN")); - cli->stop(); - QCOMPARE(cli->state(), QProcess::NotRunning); - QCOMPARE(cli->exitCode(), 0); - } - - - void changePin() - { - CLI_VERIFY(cli->run()); - CLI_VERIFY(cli->waitForOutput("ready")); - cli->send("changepin"); - CLI_VERIFY(cli->waitForOutput("Please enter old PIN")); - cli->send("123456"); - CLI_VERIFY(cli->waitForOutput("Please enter new PIN")); - cli->send("123456"); - CLI_VERIFY(cli->waitForOutput("Please enter new PIN again")); - cli->send("123456"); - CLI_VERIFY(cli->waitForOutput("Start")); - } - - - void getServerPort() - { - CLI_VERIFY(cli->run()); - CLI_VERIFY(cli->waitForOutput("ready")); - QVERIFY(cli->getServerPort() > 0); - } - - -}; - -QTEST_GUILESS_MAIN(test_UIPlugInCli) -#include "test_UIPlugInCli.moc" diff --git a/test/qt/configuration/test_ProviderConfigurationParser.cpp b/test/qt/configuration/test_ProviderConfigurationParser.cpp index af297f6..eed1940 100644 --- a/test/qt/configuration/test_ProviderConfigurationParser.cpp +++ b/test/qt/configuration/test_ProviderConfigurationParser.cpp @@ -255,7 +255,7 @@ class test_ProviderConfigurationParser QTest::newRow("mac") << desktop; QTest::newRow("linux") << desktop; QTest::newRow("android") << desktop; - QTest::newRow("ios") << 15; + QTest::newRow("ios") << 17; } diff --git a/test/qt/configuration/test_ReaderConfiguration.cpp b/test/qt/configuration/test_ReaderConfiguration.cpp index 435d41c..9fc0428 100644 --- a/test/qt/configuration/test_ReaderConfiguration.cpp +++ b/test/qt/configuration/test_ReaderConfiguration.cpp @@ -11,8 +11,8 @@ #include "ResourceLoader.h" #include -#include -#include +#include +#include using namespace governikus; @@ -72,7 +72,7 @@ class test_ReaderConfiguration QTest::addColumn("readerIcon"); QTest::addColumn("readerPattern"); - QTest::newRow("Remote Cardreader") << UsbId(0x0000, 0x0000) << "NFC-abcdef1234567890" << QStringLiteral("Smartphone als Kartenleser") << "img_RemoteReader" << "^NFC.*"; + QTest::newRow("Remote card reader") << UsbId(0x0000, 0x0000) << "NFC-abcdef1234567890" << QStringLiteral("Smartphone als Kartenleser") << "img_RemoteReader" << "^NFC.*"; QTest::newRow("REINER SCT cyberJack RFID komfort") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort" << "REINER SCT cyberJack RFID komfort" << "img_Reiner_SCT_cyberjack_RFID_komfort" << "REINER SCT cyberJack RFID komfort"; QTest::newRow("REINER SCT cyberJack RFID standard") << UsbId(0x0C4B, 0x0500) << "REINER SCT cyberJack RFID standard" << "REINER SCT cyberJack RFID standard" << "img_Reiner_SCT_cyberjack_RFID_standard" << "REINER SCT cyberJack RFID standard"; @@ -90,12 +90,12 @@ class test_ReaderConfiguration QTest::newRow("OMNIKEY 5021") << UsbId(0x076B, 0x5340) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5021-CL" << "img_HID_Omnikey_Mobile_Reader_502X_CL" << "OMNIKEY CardMan 5x21-CL 0|OMNIKEY CardMan \\(076B:5340\\) 5021 CL"; QTest::newRow("OMNIKEY 5022") << UsbId(0x076B, 0x5022) << "HID Global OMNIKEY 5022 Smart Card Reader 0" << "OMNIKEY 5022-CL" << "img_HID_Omnikey_Mobile_Reader_502X_CL" << "HID Global OMNIKEY 5022 Smart Card Reader 0|HID Global OMNIKEY 5022 Smart Card Reader"; QTest::newRow("OMNIKEY 5321 v2") << UsbId(0x076B, 0x5321) << "OOMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5321 v2" << "img_HID_Global_OMNIKEY_5321_V2" << R"(OMNIKEY CardMan 5x21-CL 0|OMNIKEY CardMan \(076B:5321\) 5321(\(1\)|\(2\)))"; - QTest::newRow("OMNIKEY 5421") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5421" << "img_HID_Omnikey_542x" << R"(OMNIKEY CardMan 5x21-CL 0|OMNIKEY CardMan \(076B:5421\) 5421(\(1\)|\(2\)))"; + QTest::newRow("OMNIKEY 5421") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5421" << "img_HID_Omnikey_542x" << R"(OMNIKEY CardMan 5x21-CL 0|OMNIKEY Smart Card Reader USB 0|OMNIKEY CardMan \(076B:5421\) 5421(\(1\)|\(2\)))"; QTest::newRow("OMNIKEY 5422") << UsbId(0x076B, 0x5422) << "HID Global OMNIKEY 5422CL Smartcard Reader 0" << "OMNIKEY 5422" << "img_HID_Omnikey_542x" << R"(HID Global OMNIKEY 5422CL Smartcard Reader 0|HID Global OMNIKEY Smartcard Reader (\(1\)|\(2\)))"; QTest::newRow("FEIG OBID myAXXESS RFID-Reader") << UsbId(0x0AB1, 0x0003) << "FEIG ELECTRONIC GmbH OBID myAXXESS basic Slot:CL 358334430" << "OBID RFID-Reader" << "img_FEIG_myAXXES_basic" << "FEIG ELECTRONIC GmbH OBID myAXXESS basic Slot:CL 358334430"; - QTest::newRow("Gemalto-Prox-DU") << UsbId(0x08E6, 0x5503) << "Gemalto Prox-DU Contactless_" << "Prox-DU HID" << "img_Gemalto_Prox_DU" << R"(Gemalto Prox(-DU| Dual)( Contactless_| USB PC Link(Reader| Reader)(\(2\)|\(1\))))"; + QTest::newRow("Gemalto-Prox-DU") << UsbId(0x08E6, 0x5503) << "Gemalto Prox-DU Contactless_" << "Prox-DU HID" << "img_Gemalto_Prox_DU" << R"(Gemalto .*Prox(-DU| Dual)( Contactless_| USB PC Link(Reader| Reader)(\(2\)|\(1\))))"; QTest::newRow("Gemalto-Prox-SU") << UsbId(0x08E6, 0x5504) << "Gemalto Prox-SU Contactless_" << "Gemalto Prox-SU Contactless" << "img_Gemalto_Prox_SU" << R"(Gemalto Prox( |-)SU( Contactless_| USB PC LinkReader(\(1\)|\(2\))))"; QTest::newRow("Identiv-SCL-3711") << UsbId(0x04E6, 0x5591) << "SCM Microsystems SCL3711 reader & NFC device 0" << "Identiv SCL3711" << "img_Identive_SCL3711" << "(SCM Microsystems SCL3711 reader & NFC device 0|SCL3711 reader and NFC device)"; @@ -138,7 +138,7 @@ class test_ReaderConfiguration QTest::newRow("UU") << UsbId(0xFFFF, 0xFFFF) << "crap" << "crap"; - QTest::newRow("Remote Cardreader") << UsbId(0x0000, 0x0000) << "NFC-abcdef1234567890" << QStringLiteral("Smartphone als Kartenleser"); + QTest::newRow("Remote card reader") << UsbId(0x0000, 0x0000) << "NFC-abcdef1234567890" << QStringLiteral("Smartphone als Kartenleser"); QTest::newRow("REINER SCT cyberJack RFID komfort-windows-7-10") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort USB 1" << "REINER SCT cyberJack RFID komfort"; QTest::newRow("REINER SCT cyberJack RFID komfort-macosx-10.11-10.14") << UsbId(0x0C4B, 0x0501) << "REINER SCT cyberJack RFID komfort" << "REINER SCT cyberJack RFID komfort"; @@ -149,7 +149,8 @@ class test_ReaderConfiguration QTest::newRow("REINER SCT cyberJack RFID basis-windows-7-10") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis 0" << "REINER SCT cyberJack RFID basis"; QTest::newRow("REINER SCT cyberJack RFID basis-macosx-10.11-10.14") << UsbId(0x0C4B, 0x9102) << "REINER SCT cyberJack RFID basis" << "REINER SCT cyberJack RFID basis"; - QTest::newRow("REINER SCT cyberJack wave-windows-7-10") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave 0" << "REINER SCT cyberJack wave"; + QTest::newRow("REINER SCT cyberJack wave-windows-7-10-1") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave 0" << "REINER SCT cyberJack wave"; + QTest::newRow("REINER SCT cyberJack wave-windows-7-10-2") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave USB 1" << "REINER SCT cyberJack wave"; QTest::newRow("REINER SCT cyberJack wave-macosx-10.11-10.14") << UsbId(0x0C4B, 0x0505) << "REINER SCT cyberJack wave" << "REINER SCT cyberJack wave"; QTest::newRow("KOBIL IDToken-windows-7-10") << UsbId(0x0D46, 0x301D) << "KOBIL IDToken 0" << "KOBIL IDToken"; @@ -157,11 +158,15 @@ class test_ReaderConfiguration QTest::newRow("SCM SDI011-windows-7-10-1") << UsbId(0x04E6, 0x512B) << "SCM Microsystems Inc. SDI011 Contactless Reader 0" << "SDI011 Contactless Reader"; QTest::newRow("SCM SDI011-windows-7-10-2") << UsbId(0x04E6, 0x512B) << "SCM Microsystems Inc. SDI011 Smart Card Reader 0" << "SCM Microsystems Inc. SDI011 Smart Card Reader 0"; - QTest::newRow("SCM SDI011-macosx-10.11-10.14") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(1)" << "SDI011 Contactless Reader"; - QTest::newRow("SCM SDI011-macosx-10.11-10.14") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(2)" << "SDI011 Contactless Reader"; + QTest::newRow("SCM SDI011-windows-7-10-3") << UsbId(0x04E6, 0x512B) << "SDI011 Contactless Reader" << "SDI011 Contactless Reader"; + QTest::newRow("SCM SDI011-macosx-10.11-10.14-1") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(1)" << "SDI011 Contactless Reader"; + QTest::newRow("SCM SDI011-macosx-10.11-10.14-2") << UsbId(0x04E6, 0x512B) << "SDI011 USB Smart Card Reader(2)" << "SDI011 Contactless Reader"; + QTest::newRow("SCM SDI011-macosx-10.10-10:14-3") << UsbId(0x04E6, 0x512B) << "SCM Microsystems Inc. SDI011 Contactless Reader(1)" << "SDI011 Contactless Reader"; - QTest::newRow("SCM SCL011-windows-7-10") << UsbId(0x04E6, 0x5292) << "SCM Microsystems Inc. SCL011 Contactless Reader 0" << "SCL01x Contactless Reader"; - QTest::newRow("SCM SCL011-macosx-10.11-10.14") << UsbId(0x04E6, 0x5292) << "SCL011 Contactless Reader" << "SCL01x Contactless Reader"; + QTest::newRow("SCM SCL011-windows-7-10-1") << UsbId(0x04E6, 0x5292) << "SCL011 Contactless Reader" << "SCL01x Contactless Reader"; + QTest::newRow("SCM SCL011-windows-7-10-2") << UsbId(0x04E6, 0x5292) << "SCM Microsystems Inc. SCL011 Contactless Reader 0" << "SCL01x Contactless Reader"; + QTest::newRow("SCM SCL011-macosx-10.11-10.14-1") << UsbId(0x04E6, 0x5292) << "SCL011 Contactless Reader" << "SCL01x Contactless Reader"; + QTest::newRow("SCM SCL011-macosx-10.11-10-14-2") << UsbId(0x04E6, 0x5292) << "SCM Microsystems Inc. SCL011 Contactless Reader" << "SCL01x Contactless Reader"; QTest::newRow("ACS-ACR1281U-windows-7-10") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader 0" << "ACS ACR1281U"; QTest::newRow("ACS-ACR1281U-macosx-10.11-10.14") << UsbId(0x072F, 0x0901) << "ACS ACR1281 PICC Reader" << "ACS ACR1281U"; @@ -184,6 +189,7 @@ class test_ReaderConfiguration QTest::newRow("OMNIKEY 5421-windows-7-10-1") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21 0" << "OMNIKEY CardMan 5x21 0"; QTest::newRow("OMNIKEY 5421-windows-7-10-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan 5x21-CL 0" << "OMNIKEY 5421"; + QTest::newRow("OMNIKEY 5421-windows-7-10-3") << UsbId(0x076B, 0x5421) << "OMNIKEY Smart Card Reader USB 0" << "OMNIKEY 5421"; QTest::newRow("OMNIKEY 5421-macosx-10.11-10.14-1") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(1)" << "OMNIKEY 5421"; QTest::newRow("OMNIKEY 5421-macosx-10.11-10.14-2") << UsbId(0x076B, 0x5421) << "OMNIKEY CardMan (076B:5421) 5421(2)" << "OMNIKEY 5421"; @@ -217,6 +223,7 @@ class test_ReaderConfiguration QTest::newRow("Identiv-Cloud-4701-F-windows-7-10-1") << UsbId(0x04E6, 0x5724) << "Identiv CLOUD 4701 F Contact Reader 0" << "Identiv CLOUD 4701 F Contact Reader 0"; QTest::newRow("Identiv-Cloud-4701-F-windows-7-10-2") << UsbId(0x04E6, 0x5724) << "Identiv CLOUD 4701 F Contactless Reader 1" << "Identiv Cloud 4701 F"; + QTest::newRow("Identiv-Cloud-4701-F-windows-7-10-3") << UsbId(0x04E6, 0x5724) << "Identiv CLOUD 4701 F Contactless Reader 0" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-macosx-10.11-10.14-1") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(1)" << "Identiv Cloud 4701 F"; QTest::newRow("Identiv-Cloud-4701-F-macosx-10.11-10.14-2") << UsbId(0x04E6, 0x5724) << "Identiv uTrust 4701 F Dual Interface Reader(2)" << "Identiv Cloud 4701 F"; diff --git a/test/qt/configuration/test_ReaderConfigurationParser.cpp b/test/qt/configuration/test_ReaderConfigurationParser.cpp index 21b34ec..0618e6a 100644 --- a/test/qt/configuration/test_ReaderConfigurationParser.cpp +++ b/test/qt/configuration/test_ReaderConfigurationParser.cpp @@ -124,7 +124,6 @@ class test_ReaderConfigurationParser void validJsonDocumentWithOneValidEntry_parseOkAndOneCorrectDeviceInfo() { const QByteArray data = QByteArrayLiteral("{" - " \"IssueDate\": \"2015-11-03T12:00:00+1:00\"," " \"SupportedDevices\":\n" " [\n" " {\n" @@ -224,7 +223,6 @@ class test_ReaderConfigurationParser void parserAcceptsJSONWithMultipleEmptyPatternsInReadersWithDifferentNames() { const QByteArray data = QByteArrayLiteral("{" - " \"IssueDate\": \"2015-11-03T12:00:00+1:00\"," " \"SupportedDevices\":\n" " [\n" " {\n" @@ -257,7 +255,6 @@ class test_ReaderConfigurationParser void parserRejectsJSONDataWithRepeatedUSBId() { const QByteArray data = QByteArrayLiteral("{" - " \"IssueDate\": \"2015-11-03T12:00:00+1:00\"," " \"SupportedDevices\":\n" " [\n" " {\n" @@ -290,7 +287,6 @@ class test_ReaderConfigurationParser void parserRejectsJSONDataWithRepeatedReaderName() { const QByteArray data = QByteArrayLiteral("{" - " \"IssueDate\": \"2015-11-03T12:00:00+1:00\"," " \"SupportedDevices\":\n" " [\n" " {\n" @@ -323,7 +319,6 @@ class test_ReaderConfigurationParser void parserRejectsJSONDataWithRepeatedPattern() { const QByteArray data = QByteArrayLiteral("{" - " \"IssueDate\": \"2015-11-03T12:00:00+1:00\"," " \"SupportedDevices\":\n" " [\n" " {\n" diff --git a/test/qt/core/context/test_WorkflowContext.cpp b/test/qt/core/context/test_WorkflowContext.cpp index 86b2f5d..feee585 100644 --- a/test/qt/core/context/test_WorkflowContext.cpp +++ b/test/qt/core/context/test_WorkflowContext.cpp @@ -17,167 +17,169 @@ class test_WorkflowContext : public QObject { Q_OBJECT + QSharedPointer mContext; private Q_SLOTS: + void init() + { + mContext.reset(new WorkflowContext()); + } + + + void cleanup() + { + mContext.clear(); + } + + void test_WorkflowFinished() { - WorkflowContext workflowContext; + mContext->setWorkflowFinished(true); + QVERIFY(mContext->isWorkflowFinished()); - workflowContext.setWorkflowFinished(true); - QVERIFY(workflowContext.isWorkflowFinished()); - - workflowContext.setWorkflowFinished(false); - QVERIFY(!workflowContext.isWorkflowFinished()); + mContext->setWorkflowFinished(false); + QVERIFY(!mContext->isWorkflowFinished()); } void test_CanAllowed() { - WorkflowContext workflowContext; - QSignalSpy spy(&workflowContext, &WorkflowContext::fireCanAllowedModeChanged); + QSignalSpy spy(mContext.data(), &WorkflowContext::fireCanAllowedModeChanged); - workflowContext.setCanAllowedMode(true); - QVERIFY(workflowContext.isCanAllowedMode()); + mContext->setCanAllowedMode(true); + QVERIFY(mContext->isCanAllowedMode()); QCOMPARE(spy.count(), 1); - workflowContext.setCanAllowedMode(false); - QVERIFY(!workflowContext.isCanAllowedMode()); + mContext->setCanAllowedMode(false); + QVERIFY(!mContext->isCanAllowedMode()); QCOMPARE(spy.count(), 2); } void test_Can() { - WorkflowContext workflowContext; const QString can1 = QStringLiteral("123256"); const QString can2 = QStringLiteral("222222"); const QString can3 = QStringLiteral("222222"); - QSignalSpy spy(&workflowContext, &WorkflowContext::fireCanChanged); + QSignalSpy spy(mContext.data(), &WorkflowContext::fireCanChanged); - workflowContext.setCan(can1); - QCOMPARE(workflowContext.getCan(), can1); + mContext->setCan(can1); + QCOMPARE(mContext->getCan(), can1); QCOMPARE(spy.count(), 1); - workflowContext.setCan(can2); - QCOMPARE(workflowContext.getCan(), can2); + mContext->setCan(can2); + QCOMPARE(mContext->getCan(), can2); QCOMPARE(spy.count(), 2); - workflowContext.setCan(can3); - QCOMPARE(workflowContext.getCan(), can2); + mContext->setCan(can3); + QCOMPARE(mContext->getCan(), can2); QCOMPARE(spy.count(), 2); } void test_Pin() { - WorkflowContext workflowContext; const QString pin1 = QStringLiteral("123256"); const QString pin2 = QStringLiteral("222222"); const QString pin3 = QStringLiteral("222222"); - QSignalSpy spy(&workflowContext, &WorkflowContext::firePinChanged); + QSignalSpy spy(mContext.data(), &WorkflowContext::firePinChanged); - workflowContext.setPin(pin1); - QCOMPARE(workflowContext.getPin(), pin1); + mContext->setPin(pin1); + QCOMPARE(mContext->getPin(), pin1); QCOMPARE(spy.count(), 1); - workflowContext.setPin(pin2); - QCOMPARE(workflowContext.getPin(), pin2); + mContext->setPin(pin2); + QCOMPARE(mContext->getPin(), pin2); QCOMPARE(spy.count(), 2); - workflowContext.setPin(pin3); - QCOMPARE(workflowContext.getPin(), pin2); + mContext->setPin(pin3); + QCOMPARE(mContext->getPin(), pin2); QCOMPARE(spy.count(), 2); } void test_Puk() { - WorkflowContext workflowContext; const QString puk1 = QStringLiteral("123256789"); const QString puk2 = QStringLiteral("222222222"); const QString puk3 = QStringLiteral("222222222"); - QSignalSpy spy(&workflowContext, &WorkflowContext::firePukChanged); + QSignalSpy spy(mContext.data(), &WorkflowContext::firePukChanged); - workflowContext.setPuk(puk1); - QCOMPARE(workflowContext.getPuk(), puk1); + mContext->setPuk(puk1); + QCOMPARE(mContext->getPuk(), puk1); QCOMPARE(spy.count(), 1); - workflowContext.setPuk(puk2); - QCOMPARE(workflowContext.getPuk(), puk2); + mContext->setPuk(puk2); + QCOMPARE(mContext->getPuk(), puk2); QCOMPARE(spy.count(), 2); - workflowContext.setPuk(puk3); - QCOMPARE(workflowContext.getPuk(), puk2); + mContext->setPuk(puk3); + QCOMPARE(mContext->getPuk(), puk2); QCOMPARE(spy.count(), 2); } void test_ErrorReportToUser() { - WorkflowContext workflowContext; + mContext->setErrorReportedToUser(true); + QVERIFY(mContext->isErrorReportedToUser()); - workflowContext.setErrorReportedToUser(true); - QVERIFY(workflowContext.isErrorReportedToUser()); - - workflowContext.setErrorReportedToUser(false); - QVERIFY(!workflowContext.isErrorReportedToUser()); + mContext->setErrorReportedToUser(false); + QVERIFY(!mContext->isErrorReportedToUser()); } void test_CurrentState() { - WorkflowContext workflowContext; const QString state1 = QStringLiteral("state1"); const QString state2 = QStringLiteral("state2"); - QSignalSpy spy(&workflowContext, &WorkflowContext::fireStateChanged); + QSignalSpy spy(mContext.data(), &WorkflowContext::fireStateChanged); - workflowContext.setCurrentState(state1); - QCOMPARE(workflowContext.getCurrentState(), state1); + mContext->setCurrentState(state1); + QCOMPARE(mContext->getCurrentState(), state1); QCOMPARE(spy.count(), 1); - QVERIFY(!workflowContext.isStateApproved()); + QVERIFY(!mContext->isStateApproved()); - workflowContext.setCurrentState(state2); - QCOMPARE(workflowContext.getCurrentState(), state2); + mContext->setCurrentState(state2); + QCOMPARE(mContext->getCurrentState(), state2); QCOMPARE(spy.count(), 2); - QVERIFY(!workflowContext.isStateApproved()); + QVERIFY(!mContext->isStateApproved()); - workflowContext.killWorkflow(); - QCOMPARE(workflowContext.getCurrentState(), state2); + mContext->killWorkflow(); + QCOMPARE(mContext->getCurrentState(), state2); QCOMPARE(spy.count(), 2); - QVERIFY(workflowContext.isStateApproved()); + QVERIFY(mContext->isStateApproved()); } void test_ReaderPlugInTypes() { - WorkflowContext workflowContext; QVector vector1({ReaderManagerPlugInType::PCSC}); QVector vector2({ReaderManagerPlugInType::BLUETOOTH}); - QSignalSpy spy(&workflowContext, &WorkflowContext::fireReaderPlugInTypesChanged); + QSignalSpy spy(mContext.data(), &WorkflowContext::fireReaderPlugInTypesChanged); - workflowContext.setReaderPlugInTypes(vector1); - QCOMPARE(workflowContext.getReaderPlugInTypes(), vector1); + mContext->setReaderPlugInTypes(vector1); + QCOMPARE(mContext->getReaderPlugInTypes(), vector1); QCOMPARE(spy.count(), 1); spy.clear(); - workflowContext.setReaderPlugInTypes(vector2); - QCOMPARE(workflowContext.getReaderPlugInTypes(), vector2); + mContext->setReaderPlugInTypes(vector2); + QCOMPARE(mContext->getReaderPlugInTypes(), vector2); QCOMPARE(spy.count(), 1); } void test_LastPaceAndResult() { - WorkflowContext workflowContext; - QSignalSpy spy(&workflowContext, &WorkflowContext::fireLastPaceResultChanged); + QSignalSpy spy(mContext.data(), &WorkflowContext::firePaceResultUpdated); - workflowContext.setLastPaceResult(CardReturnCode::COMMAND_FAILED); - QCOMPARE(workflowContext.getLastPaceResult(), CardReturnCode::COMMAND_FAILED); + mContext->setLastPaceResult(CardReturnCode::COMMAND_FAILED); + QCOMPARE(mContext->getLastPaceResult(), CardReturnCode::COMMAND_FAILED); QCOMPARE(spy.count(), 1); - workflowContext.setLastPaceResult(CardReturnCode::OK); - QCOMPARE(workflowContext.getLastPaceResult(), CardReturnCode::OK); + mContext->setLastPaceResult(CardReturnCode::OK); + QCOMPARE(mContext->getLastPaceResult(), CardReturnCode::OK); QCOMPARE(spy.count(), 2); } @@ -186,8 +188,7 @@ class test_WorkflowContext { QThread cardThread; cardThread.start(); - WorkflowContext workflowContext; - QSignalSpy spy(&workflowContext, &WorkflowContext::fireCardConnectionChanged); + QSignalSpy spy(mContext.data(), &WorkflowContext::fireCardConnectionChanged); MockReader reader1(QStringLiteral("reader")); reader1.moveToThread(&cardThread); @@ -201,12 +202,12 @@ class test_WorkflowContext worker2->moveToThread(&cardThread); QSharedPointer cardConnection2(new CardConnection(worker2)); - workflowContext.setCardConnection(cardConnection1); - QCOMPARE(workflowContext.getCardConnection(), cardConnection1); + mContext->setCardConnection(cardConnection1); + QCOMPARE(mContext->getCardConnection(), cardConnection1); QCOMPARE(spy.count(), 1); - workflowContext.setCardConnection(cardConnection2); - QCOMPARE(workflowContext.getCardConnection(), cardConnection2); + mContext->setCardConnection(cardConnection2); + QCOMPARE(mContext->getCardConnection(), cardConnection2); QCOMPARE(spy.count(), 2); cardThread.quit(); @@ -221,24 +222,18 @@ class test_WorkflowContext const QSharedPointer worker(new MockCardConnectionWorker()); worker->moveToThread(&workerThread); - const QSharedPointer connection1(new CardConnection(worker)); - const QSharedPointer connection2(new CardConnection(worker)); const CardInfo cardInfo1(CardType::EID_CARD, QSharedPointer(), 3, false, false); const CardInfo cardInfo2(CardType::EID_CARD, QSharedPointer(), 0, false, false); const ReaderInfo readerInfo1(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo1); const ReaderInfo readerInfo2(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo2); - connection1->onReaderInfoChanged(readerInfo1); - connection2->onReaderInfoChanged(readerInfo2); + QVERIFY(!mContext->isPinBlocked()); - WorkflowContext context; - QVERIFY(!context.isPinBlocked()); + mContext->setCardConnection(QSharedPointer::create(worker)); + Q_EMIT worker->fireReaderInfoChanged(readerInfo1); - context.setCardConnection(connection1); - QVERIFY(!context.isPinBlocked()); - - context.setCardConnection(connection2); - QVERIFY(context.isPinBlocked()); + Q_EMIT worker->fireReaderInfoChanged(readerInfo2); + QVERIFY(mContext->isPinBlocked()); workerThread.quit(); workerThread.wait(); @@ -247,28 +242,25 @@ class test_WorkflowContext void test_WorkflowKilled() { - WorkflowContext context; - QSignalSpy spy(&context, &WorkflowContext::fireCancelWorkflow); + QSignalSpy spy(mContext.data(), &WorkflowContext::fireCancelWorkflow); - QVERIFY(!context.isWorkflowKilled()); + QVERIFY(!mContext->isWorkflowKilled()); QTest::ignoreMessage(QtWarningMsg, "Killing the current workflow."); - context.killWorkflow(); - QVERIFY(context.isWorkflowKilled()); - QCOMPARE(context.getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); - QVERIFY(context.isStateApproved()); + mContext->killWorkflow(); + QVERIFY(mContext->isWorkflowKilled()); + QCOMPARE(mContext->getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); + QVERIFY(mContext->isStateApproved()); QCOMPARE(spy.count(), 1); } void test_IsWorkflowCancelled() { - WorkflowContext context; + QVERIFY(!mContext->isWorkflowCancelled()); - QVERIFY(!context.isWorkflowCancelled()); - - context.mWorkflowCancelled = true; - QVERIFY(context.isWorkflowCancelled()); + Q_EMIT mContext->fireCancelWorkflow(); + QVERIFY(mContext->isWorkflowCancelled()); } diff --git a/test/qt/core/controller/test_AppController.cpp b/test/qt/core/controller/test_AppController.cpp new file mode 100644 index 0000000..e1ec13d --- /dev/null +++ b/test/qt/core/controller/test_AppController.cpp @@ -0,0 +1,147 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "controller/AppController.h" + +#include "context/ChangePinContext.h" +#include "context/RemoteServiceContext.h" +#include "controller/ChangePinController.h" +#include "controller/RemoteServiceController.h" + +#include "MockActivationContext.h" + +#include +#include + + +using namespace governikus; + + +class test_AppController + : public QObject +{ + Q_OBJECT + QSharedPointer mController; + + private Q_SLOTS: + void init() + { + mController.reset(new AppController()); + } + + + void cleanup() + { + mController.clear(); + } + + + void test_StartNewWorkflow() + { + QSignalSpy spy(mController.data(), &AppController::fireWorkflowStarted); + const QSharedPointer context(new ChangePinContext()); + QTest::ignoreMessage(QtInfoMsg, "Starting new workflow PIN"); + QTest::ignoreMessage(QtDebugMsg, "Start governikus::ChangePinController"); + QVERIFY(mController->startNewWorkflow(Action::PIN, context)); + QCOMPARE(mController->mActiveController->getContext(), context); + QCOMPARE(mController->mCurrentAction, Action::PIN); + QCOMPARE(spy.count(), 1); + + QTest::ignoreMessage(QtWarningMsg, "Cannot start governikus::ChangePinController | Current action: PIN"); + QVERIFY(!mController->startNewWorkflow(Action::PIN, context)); + } + + + void test_OnWorkflowFinishedNoWaitingRequest() + { + QSignalSpy spyWorkflowFinished(mController.data(), &AppController::fireWorkflowFinished); + + mController->onRemoteServiceRequested(); + QTest::ignoreMessage(QtDebugMsg, "governikus::RemoteServiceController done"); + QTest::ignoreMessage(QtInfoMsg, "Finished workflow REMOTE_SERVICE"); + mController->onWorkflowFinished(); + QCOMPARE(spyWorkflowFinished.count(), 1); + } + + + void test_OnWorkflowFinishedPIN() + { + QSignalSpy spyWorkflowFinished(mController.data(), &AppController::fireWorkflowFinished); + + mController->onChangePinRequested(); + mController->onChangePinRequested(); + QTest::ignoreMessage(QtDebugMsg, "governikus::ChangePinController done"); + QTest::ignoreMessage(QtInfoMsg, "Finished workflow PIN"); + QTest::ignoreMessage(QtDebugMsg, "Running waiting action now."); + QTest::ignoreMessage(QtInfoMsg, "Starting new workflow PIN"); + mController->onWorkflowFinished(); + QCOMPARE(spyWorkflowFinished.count(), 1); + QVERIFY(!mController->mWaitingRequest); + QCOMPARE(mController->mCurrentAction, Action::PIN); + } + + + void test_OnWorkflowFinishedAUTH() + { + QSignalSpy spyWorkflowFinished(mController.data(), &AppController::fireWorkflowFinished); + + QSharedPointer context(new MockActivationContext()); + mController->onSelfAuthenticationRequested(); + mController->mActiveController->getContext()->setWorkflowFinished(true); + mController->onAuthenticationRequest(context); + QTest::ignoreMessage(QtDebugMsg, "governikus::SelfAuthController done"); + QTest::ignoreMessage(QtInfoMsg, "Finished workflow SELF"); + QTest::ignoreMessage(QtDebugMsg, "Running waiting action now."); + QTest::ignoreMessage(QtInfoMsg, "Starting new workflow AUTH"); + mController->onWorkflowFinished(); + QCOMPARE(spyWorkflowFinished.count(), 1); + QVERIFY(!mController->mWaitingRequest); + QCOMPARE(mController->mCurrentAction, Action::AUTH); + } + + + void test_OnAuthenticationRequestSELF() + { + const QSharedPointer context(new MockActivationContext()); + + mController->onSelfAuthenticationRequested(); + mController->mActiveController->getContext()->setWorkflowFinished(true); + QTest::ignoreMessage(QtDebugMsg, "Authentication requested"); + QTest::ignoreMessage(QtDebugMsg, "Auto-approving the current state"); + mController->onAuthenticationRequest(context); + QVERIFY(mController->mActiveController->getContext()->isStateApproved()); + + mController->onSelfAuthenticationRequested(); + QTest::ignoreMessage(QtWarningMsg, "Cannot enqueue action AUTH , queue is already full."); + mController->onAuthenticationRequest(context); + } + + + void test_OnAuthenticationRequestCannotSendOperationAlreadyActive() + { + const QString sendError("send error"); + const QSharedPointer context(new MockActivationContext(false, false, false, false, sendError)); + mController->onChangePinRequested(); + QTest::ignoreMessage(QtCriticalMsg, R"(Cannot send "Operation already active" to caller: "send error")"); + mController->onAuthenticationRequest(context); + } + + + void test_DoShutdownActiveController() + { + QSignalSpy spyHideUi(mController.data(), &AppController::fireHideUi); + + mController->onChangePinRequested(); + mController->onChangePinRequested(); + mController->doShutdown(); + QVERIFY(!mController->mWaitingRequest); + QVERIFY(mController->mActiveController->getContext()->isWorkflowKilled()); + QCOMPARE(spyHideUi.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_AppController) +#include "test_AppController.moc" diff --git a/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC2.cpp b/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC2.cpp index 8f35446..8ea2498 100644 --- a/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC2.cpp +++ b/test/qt/core/paos/invoke/test_DidAuthenticateResponseEAC2.cpp @@ -8,8 +8,8 @@ #include "TestFileHelper.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/core/paos/invoke/test_PaosCreator.cpp b/test/qt/core/paos/invoke/test_PaosCreator.cpp index 77add00..dde6d5d 100644 --- a/test/qt/core/paos/invoke/test_PaosCreator.cpp +++ b/test/qt/core/paos/invoke/test_PaosCreator.cpp @@ -13,6 +13,8 @@ using namespace governikus; +Q_DECLARE_METATYPE(PaosCreator::Namespace) + namespace { struct test_PaosCreatorDummy @@ -98,20 +100,36 @@ class test_PaosCreator } + void namespaces_data() + { + QTest::addColumn("namespaceName"); + QTest::addColumn("suffix"); + QTest::addColumn("namespaceString"); + QTest::addColumn("prefixString"); + QTest::addColumn("type"); + + QTest::newRow("addressing") << PaosCreator::Namespace::ADDRESSING << QString("test") << QString("http://www.w3.org/2005/03/addressing") << QString("wsa:test") << QString("wsa:test"); + QTest::newRow("dss") << PaosCreator::Namespace::DSS << QString() << QString("urn:oasis:names:tc:dss:1.0:core:schema") << QString("xmlns:dss") << QString("dss:"); + QTest::newRow("ecard") << PaosCreator::Namespace::ECARD << QString("ecard") << QString("http://www.bsi.bund.de/ecard/api/1.1") << QString("ecard:ecard") << QString("ecard:ecard"); + QTest::newRow("paos") << PaosCreator::Namespace::PAOS << QString("") << QString("urn:liberty:paos:2006-08") << QString("paos:") << QString("paos:"); + QTest::newRow("techschema") << PaosCreator::Namespace::TECHSCHEMA << QString(" ") << QString("urn:iso:std:iso-iec:24727:tech:schema") << QString("iso: ") << QString("iso: "); + QTest::newRow("xsd") << PaosCreator::Namespace::XSD << QString() << QString("http://www.w3.org/2001/XMLSchema") << QString("xmlns:xsd") << QString("xsd:"); + QTest::newRow("xsi") << PaosCreator::Namespace::XSI << QString() << QString("http://www.w3.org/2001/XMLSchema-instance") << QString("xmlns:xsi") << QString("xsi:"); + QTest::newRow("soap") << PaosCreator::Namespace::SOAP << QString("test") << QString("http://schemas.xmlsoap.org/soap/envelope/") << QString("soap:test") << QString("soap:test"); + } + + void namespaces() { - static int count = 8; + QFETCH(PaosCreator::Namespace, namespaceName); + QFETCH(QString, suffix); + QFETCH(QString, namespaceString); + QFETCH(QString, prefixString); + QFETCH(QString, type); - QCOMPARE(PaosCreator::mNamespace.count(), count); - QCOMPARE(PaosCreator::mNamespacePrefix.count(), count + 1); // + DEFAULT - - QCOMPARE(PaosCreator::mNamespace.value(PaosCreator::Namespace::ADDRESSING), QString("http://www.w3.org/2005/03/addressing")); - QCOMPARE(PaosCreator::mNamespacePrefix.value(PaosCreator::Namespace::ADDRESSING), QString("wsa")); - - QCOMPARE(PaosCreator::getNamespace(PaosCreator::Namespace::ADDRESSING), QString("http://www.w3.org/2005/03/addressing")); - QCOMPARE(PaosCreator::getNamespacePrefix(PaosCreator::Namespace::ADDRESSING, "suffix"), QString("wsa:suffix")); - QCOMPARE(PaosCreator::getNamespacePrefix(PaosCreator::Namespace::ADDRESSING), QString("xmlns:wsa")); - QCOMPARE(PaosCreator::getNamespaceType(PaosCreator::Namespace::ADDRESSING, "test"), QString("wsa:test")); + QCOMPARE(PaosCreator::getNamespace(namespaceName), namespaceString); + QCOMPARE(PaosCreator::getNamespacePrefix(namespaceName, suffix), prefixString); + QCOMPARE(PaosCreator::getNamespaceType(namespaceName, suffix), type); } diff --git a/test/qt/core/paos/retrieve/test_DidAuthenticateEac1.cpp b/test/qt/core/paos/retrieve/test_DidAuthenticateEac1.cpp index 191ca1f..5eb7d76 100644 --- a/test/qt/core/paos/retrieve/test_DidAuthenticateEac1.cpp +++ b/test/qt/core/paos/retrieve/test_DidAuthenticateEac1.cpp @@ -8,8 +8,8 @@ #include "paos/retrieve/DidAuthenticateEac1Parser.h" #include "TestFileHelper.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/core/paos/retrieve/test_DidAuthenticateEac2.cpp b/test/qt/core/paos/retrieve/test_DidAuthenticateEac2.cpp index 50a754e..2df3739 100644 --- a/test/qt/core/paos/retrieve/test_DidAuthenticateEac2.cpp +++ b/test/qt/core/paos/retrieve/test_DidAuthenticateEac2.cpp @@ -8,8 +8,8 @@ #include "paos/retrieve/DidAuthenticateEac2Parser.h" #include "TestFileHelper.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/core/paos/retrieve/test_DidAuthenticateEacAdditional.cpp b/test/qt/core/paos/retrieve/test_DidAuthenticateEacAdditional.cpp index 445d797..a706187 100644 --- a/test/qt/core/paos/retrieve/test_DidAuthenticateEacAdditional.cpp +++ b/test/qt/core/paos/retrieve/test_DidAuthenticateEacAdditional.cpp @@ -7,8 +7,8 @@ #include "paos/retrieve/DidAuthenticateEacAdditional.h" #include "TestFileHelper.h" -#include -#include +#include +#include #include "paos/retrieve/DidAuthenticateEacAdditionalParser.h" diff --git a/test/qt/core/paos/retrieve/test_InitializeFramework.cpp b/test/qt/core/paos/retrieve/test_InitializeFramework.cpp index 1d4aaa4..d30a25f 100644 --- a/test/qt/core/paos/retrieve/test_InitializeFramework.cpp +++ b/test/qt/core/paos/retrieve/test_InitializeFramework.cpp @@ -7,8 +7,8 @@ #include "paos/retrieve/InitializeFramework.h" #include "TestFileHelper.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/core/paos/retrieve/test_StartPAOSResponse.cpp b/test/qt/core/paos/retrieve/test_StartPAOSResponse.cpp index dbdfd7b..7518a16 100644 --- a/test/qt/core/paos/retrieve/test_StartPAOSResponse.cpp +++ b/test/qt/core/paos/retrieve/test_StartPAOSResponse.cpp @@ -7,8 +7,8 @@ #include "paos/retrieve/StartPaosResponse.h" #include "TestFileHelper.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/core/paos/retrieve/test_transmit.cpp b/test/qt/core/paos/retrieve/test_transmit.cpp index d7d2f2f..f77a89d 100644 --- a/test/qt/core/paos/retrieve/test_transmit.cpp +++ b/test/qt/core/paos/retrieve/test_transmit.cpp @@ -8,8 +8,8 @@ #include "paos/retrieve/TransmitParser.h" #include "TestFileHelper.h" -#include -#include +#include +#include using namespace governikus; diff --git a/test/qt/core/paos/test_RequestType.cpp b/test/qt/core/paos/test_RequestType.cpp index 94daa80..6d7b993 100644 --- a/test/qt/core/paos/test_RequestType.cpp +++ b/test/qt/core/paos/test_RequestType.cpp @@ -4,8 +4,8 @@ * \copyright Copyright (c) 2015-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include "paos/RequestType.h" diff --git a/test/qt/core/paos/test_paoshandler.cpp b/test/qt/core/paos/test_paoshandler.cpp index 6ff84df..a23ef88 100644 --- a/test/qt/core/paos/test_paoshandler.cpp +++ b/test/qt/core/paos/test_paoshandler.cpp @@ -4,8 +4,8 @@ #include #include -#include -#include +#include +#include #include "paos/PaosHandler.h" #include "TestFileHelper.h" diff --git a/test/qt/core/states/test_StateCertificateDescriptionCheck.cpp b/test/qt/core/states/test_StateCertificateDescriptionCheck.cpp index 2ee403b..92e77a1 100644 --- a/test/qt/core/states/test_StateCertificateDescriptionCheck.cpp +++ b/test/qt/core/states/test_StateCertificateDescriptionCheck.cpp @@ -7,8 +7,11 @@ #include "asn1/ASN1Util.h" #include "asn1/CVCertificate.h" #include "context/AuthContext.h" +#include "states/StateBuilder.h" #include "TestAuthContext.h" +#include "TestFileHelper.h" + #include #include #include @@ -32,7 +35,7 @@ class test_StateCertificateDescriptionCheck mAuthContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1.xml")); mAuthContext->setTcTokenUrl(QUrl("https://dev-demo.governikus-eid.de:8443/Autent-DemoApplication/RequestServlet;jsessionid=14w5aKuENyd2D4ZsMmuaeX2g")); - mState.reset(new StateCertificateDescriptionCheck(mAuthContext)); + mState.reset(StateBuilder::createState(mAuthContext)); mState->setStateName("StateCertificateDescriptionCheck"); connect(this, &test_StateCertificateDescriptionCheck::fireStateStart, mState.data(), &AbstractState::onEntry, Qt::ConnectionType::DirectConnection); @@ -48,11 +51,13 @@ class test_StateCertificateDescriptionCheck void noDescription() { - mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCertificateDescription = QSharedPointer(new CertificateDescription()); - mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCertificateDescriptionAsBinary.clear(); - + const QSharedPointer eac1(new DIDAuthenticateEAC1()); + mAuthContext->setDidAuthenticateEac1(eac1); QSignalSpy spy(mState.data(), &StateCertificateDescriptionCheck::fireAbort); + Q_EMIT fireStateStart(nullptr); + + QTest::ignoreMessage(QtCriticalMsg, "No certificate description available"); mAuthContext->setStateApproved(); QCOMPARE(spy.count(), 1); @@ -61,11 +66,13 @@ class test_StateCertificateDescriptionCheck void noSubjectUrl() { - const auto& desc = qSharedPointerConstCast(mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCertificateDescription); + const auto& desc = qSharedPointerConstCast(mAuthContext->getDidAuthenticateEac1()->getCertificateDescription()); desc->setSubjectUrl(QString()); QSignalSpy spy(mState.data(), &StateCertificateDescriptionCheck::fireAbort); Q_EMIT fireStateStart(nullptr); + + QTest::ignoreMessage(QtCriticalMsg, "No subject url available in certificate description"); mAuthContext->setStateApproved(); QCOMPARE(spy.count(), 1); @@ -74,11 +81,14 @@ class test_StateCertificateDescriptionCheck void nonMatchingDescription() { - mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCertificateDescription = QSharedPointer(new CertificateDescription()); - mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCertificateDescriptionAsBinary = QByteArray::fromHex("1234567890abcdef"); + QByteArray source = TestFileHelper::readFile(":/card/cvca-DECVCAeID00103.hex"); + auto cert = CVCertificate::fromHex(source); + mAuthContext->setTerminalCvc(cert); QSignalSpy spy(mState.data(), &StateCertificateDescriptionCheck::fireAbort); Q_EMIT fireStateStart(nullptr); + + QTest::ignoreMessage(QtCriticalMsg, "\"The certificate description does not match the certificate.\""); mAuthContext->setStateApproved(); QCOMPARE(spy.count(), 1); @@ -89,6 +99,8 @@ class test_StateCertificateDescriptionCheck { QSignalSpy spy(mState.data(), &StateCertificateDescriptionCheck::fireContinue); Q_EMIT fireStateStart(nullptr); + + QTest::ignoreMessage(QtDebugMsg, "SOP-Check succeeded."); mAuthContext->setStateApproved(); QCOMPARE(spy.count(), 1); @@ -101,6 +113,8 @@ class test_StateCertificateDescriptionCheck QSignalSpy spy(mState.data(), &StateCertificateDescriptionCheck::fireAbort); Q_EMIT fireStateStart(nullptr); + + QTest::ignoreMessage(QtDebugMsg, "SOP-Check failed."); mAuthContext->setStateApproved(); QCOMPARE(spy.count(), 1); diff --git a/test/qt/core/states/test_StateChangePinRemote.cpp b/test/qt/core/states/test_StateChangePinRemote.cpp index 284da93..dbef5c7 100644 --- a/test/qt/core/states/test_StateChangePinRemote.cpp +++ b/test/qt/core/states/test_StateChangePinRemote.cpp @@ -9,11 +9,35 @@ #include "MockCardConnectionWorker.h" #include "MockRemoteServer.h" -#include +#include using namespace governikus; +class MockSetEidPinCommand + : public SetEidPinCommand +{ + Q_OBJECT + + public: + MockSetEidPinCommand(const QSharedPointer& pWorker, + const QString& pNewPin) + : SetEidPinCommand(pWorker, pNewPin, '0') + { + mResponseApdu = ResponseApdu(StatusCode::SUCCESS); + } + + + ~MockSetEidPinCommand() override = default; + + void internalExecute() override + { + } + + +}; + + class test_StateChangePinRemote : public QObject { @@ -34,27 +58,45 @@ class test_StateChangePinRemote workerThread.start(); const QSharedPointer context(new RemoteServiceContext()); - const QString pin = QStringLiteral("103050"); - context->setPin(pin); + StateChangePinRemote state(context); const QString slotHandle = QStringLiteral("slot"); const QByteArray input("input"); const QSharedPointer message(new IfdModifyPin(slotHandle, input)); + context->setModifyPinMessage(message); + + QSignalSpy spyContinue(&state, &StateChangePinRemote::fireContinue); + state.run(); + QCOMPARE(context->getModifyPinMessageResponseApdu().getReturnCode(), StatusCode::EMPTY); + QCOMPARE(spyContinue.count(), 1); + const QSharedPointer worker(new MockCardConnectionWorker()); worker->moveToThread(&workerThread); const QSharedPointer connection(new CardConnection(worker)); context->setCardConnection(connection); - StateChangePinRemote state(context); - context->setModifyPinMessage(message); - state.run(); QCOMPARE(state.mConnections.size(), 1); - QCOMPARE(context->getPin(), QString("103050")); workerThread.quit(); workerThread.wait(); } + void test_OnChangePinDone() + { + const QSharedPointer context(new RemoteServiceContext()); + StateChangePinRemote state(context); + + const QSharedPointer worker(new MockCardConnectionWorker()); + const QString pin("000000"); + const QSharedPointer setEidCommand(new MockSetEidPinCommand(worker, pin)); + QSignalSpy spyContinue(&state, &StateChangePinRemote::fireContinue); + + state.onChangePinDone(setEidCommand); + QCOMPARE(context->getModifyPinMessageResponseApdu().getReturnCode(), StatusCode::SUCCESS); + QCOMPARE(spyContinue.count(), 1); + } + + }; QTEST_GUILESS_MAIN(test_StateChangePinRemote) diff --git a/test/qt/core/states/test_StateCheckRefreshAddress.cpp b/test/qt/core/states/test_StateCheckRefreshAddress.cpp index cf10be9..446e597 100644 --- a/test/qt/core/states/test_StateCheckRefreshAddress.cpp +++ b/test/qt/core/states/test_StateCheckRefreshAddress.cpp @@ -7,14 +7,13 @@ #include "AppSettings.h" #include "context/AuthContext.h" #include "Env.h" -#include "MockNetworkManager.h" +#include "paos/retrieve/DidAuthenticateEac1Parser.h" #include "states/StateBuilder.h" -#include -#include -#include +#include "MockNetworkManager.h" +#include "TestFileHelper.h" + #include -#include using namespace governikus; @@ -30,6 +29,26 @@ class test_StateCheckRefreshAddress Q_SIGNALS: void fireStateStart(QEvent* pEvent); + private: + static QSharedPointer createTcToken(const QByteArray& redirectUrl) + { + QByteArray begin("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " "); + QByteArray end("" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + ""); + QByteArray data = begin.append(redirectUrl).append(end); + return QSharedPointer(new TcToken(data)); + } + + private Q_SLOTS: void init() { @@ -50,6 +69,66 @@ class test_StateCheckRefreshAddress } + void runNoTcToken() + { + QSignalSpy spyContinue(mState.data(), &StateCheckRefreshAddress::fireContinue); + + QTest::ignoreMessage(QtDebugMsg, "Invalid TCToken"); + mState->run(); + QCOMPARE(spyContinue.count(), 1); + } + + + void run_data() + { + QTest::addColumn("redirectUrl"); + QTest::addColumn("tcTokenUrl"); + QTest::addColumn("messages"); + + const QStringList msg1(QString("Invalid RefreshAddress: test")); + const QStringList msg2(QString("Invalid RefreshAddress: http://service.example.de/loggedin?7eb39f62")); + const QStringList msg3 = {QString("Subject URL from AT CVC (eService certificate) description: \"https://service.example.de\""), QString("Current redirect URL: \"https://service.example.de/loggedin?7eb39f62\""), QString("SOP-Check succeeded, abort process.")}; + const QStringList msg4 = {QString("Subject URL from AT CVC (eService certificate) description: \"https://test.de\""), QString("Current redirect URL: \"https://service.example.de/loggedin?7eb39f62\""), QString("SOP-Check failed, start process.")}; + + QTest::newRow("urlInvalid") << QByteArray("test") << QString() << msg1; + QTest::newRow("notHttps") << QByteArray("http://service.example.de/loggedin?7eb39f62") << QString("http://service.example.de") << msg2; + QTest::newRow("matchingSameOriginPolicy") << QByteArray("https://service.example.de/loggedin?7eb39f62") << QString("https://service.example.de") << msg3; + QTest::newRow("notMatchingSameOriginPolicy") << QByteArray("https://service.example.de/loggedin?7eb39f62") << QString("https://test.de") << msg4; + } + + + void run() + { + QFETCH(QByteArray, redirectUrl); + QFETCH(QString, tcTokenUrl); + QFETCH(QStringList, messages); + + QSignalSpy spyContinue(mState.data(), &StateCheckRefreshAddress::fireContinue); + + const auto& tcToken = createTcToken(redirectUrl); + const QUrl url(tcTokenUrl); + QString urlAsString(QString::fromUtf8(redirectUrl)); + QUrl redirectAddress(urlAsString); + mAuthContext->setTcToken(tcToken); + mAuthContext->setTcTokenUrl(url); + + for (const auto& msg : messages) + { + QTest::ignoreMessage(QtDebugMsg, msg.toLocal8Bit().data()); + } + mState->run(); + QCOMPARE(mState->mUrl, redirectAddress); + if (redirectAddress.isValid() && redirectAddress.scheme() == QLatin1String("https")) + { + QCOMPARE(mState->mSubjectUrl, url); + } + else + { + QCOMPARE(spyContinue.count(), 1); + } + } + + void mappingToCommunicationError() { const QVector states = QVector() @@ -57,7 +136,7 @@ class test_StateCheckRefreshAddress << GlobalStatus::Code::Network_Ssl_Establishment_Error << GlobalStatus::Code::Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length << GlobalStatus::Code::Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length - << GlobalStatus::Code::Workflow_Nerwork_Ssl_Hash_Not_In_Certificate_Description + << GlobalStatus::Code::Workflow_Network_Ssl_Hash_Not_In_Certificate_Description << GlobalStatus::Code::Workflow_Network_Empty_Redirect_Url << GlobalStatus::Code::Workflow_Network_Expected_Redirect << GlobalStatus::Code::Workflow_Network_Invalid_Scheme @@ -97,17 +176,8 @@ class test_StateCheckRefreshAddress void abortIfRefreshAddressIsNotHttps() { - QSharedPointer tcToken(new TcToken("" - "" - " https://eid-server.example.de/entrypoint" - " 1A2BB129" - " http://service.example.de/loggedin?7eb39f62" - " urn:liberty:paos:2006-08 " - " urn:ietf:rfc:4279 " - " " - " 4BC1A0B5 " - " " - "")); + const QByteArray& redirectAddress("http://service.example.de/loggedin?7eb39f62"); + const auto& tcToken = createTcToken(redirectAddress); mAuthContext->setTcToken(tcToken); QSignalSpy spy(mState.data(), &StateCheckRefreshAddress::fireContinue); @@ -146,10 +216,16 @@ class test_StateCheckRefreshAddress const QUrl tcTokenUrl("http://test/"); mAuthContext->setTcTokenUrl(tcTokenUrl); + QByteArray content = TestFileHelper::readFile(":/paos/DIDAuthenticateEAC1.xml"); + QSharedPointer eac1(static_cast(DidAuthenticateEac1Parser().parse(content))); + Env::getSingleton()->getGeneralSettings().setDeveloperMode(false); QTest::ignoreMessage(QtWarningMsg, "No subjectURL/certificate description available, take the TcToken-URL instead"); QCOMPARE(mState->determineSubjectUrl(), tcTokenUrl); + mAuthContext->setDidAuthenticateEac1(eac1); + QCOMPARE(mState->determineSubjectUrl(), QUrl("https://dev-demo.governikus-eid.de:8443")); + Env::getSingleton()->getGeneralSettings().setDeveloperMode(true); QCOMPARE(mState->determineSubjectUrl(), tcTokenUrl); } diff --git a/test/qt/core/states/test_StateConnectCard.cpp b/test/qt/core/states/test_StateConnectCard.cpp index df9ee30..e987695 100644 --- a/test/qt/core/states/test_StateConnectCard.cpp +++ b/test/qt/core/states/test_StateConnectCard.cpp @@ -6,7 +6,11 @@ #include "states/StateConnectCard.h" +#include "Env.h" +#include "ReaderManager.h" + #include "MockCardConnectionWorker.h" +#include "MockReader.h" #include @@ -16,27 +20,52 @@ class test_StateConnectCard : public QObject { Q_OBJECT + QSharedPointer mState; + QSharedPointer mContext; + ReaderInfo mReaderInfo; private Q_SLOTS: + void init() + { + mReaderInfo = ReaderInfo(QString("test"), ReaderManagerPlugInType::UNKNOWN, CardInfo(CardType::EID_CARD)); + + mContext.reset(new WorkflowContext()); + mState.reset(new StateConnectCard(mContext)); + } + + + void cleanup() + { + mContext.clear(); + mState.clear(); + } + + void test_OnCommandDone() { QThread workerThread; workerThread.start(); - const QSharedPointer context(new WorkflowContext()); - StateConnectCard connectCard(context); const QString rName("reader name"); const QSharedPointer command(new CreateCardConnectionCommand(rName, QPointer())); - QSignalSpy spyContinue(&connectCard, &StateConnectCard::fireContinue); + QSignalSpy spyContinue(mState.data(), &StateConnectCard::fireContinue); + QSignalSpy spyAbort(mState.data(), &StateConnectCard::fireAbort); + + QTest::ignoreMessage(QtDebugMsg, "Card connection command completed"); + QTest::ignoreMessage(QtDebugMsg, "Card connection failed"); + mState->onCommandDone(command); + QCOMPARE(spyAbort.count(), 1); QSharedPointer connectionWorker(new MockCardConnectionWorker()); connectionWorker->moveToThread(&workerThread); QSharedPointer cardConnection(new CardConnection(connectionWorker)); command->mCardConnection = cardConnection; + + QTest::ignoreMessage(QtDebugMsg, "Card connection command completed"); QTest::ignoreMessage(QtDebugMsg, "Card connection was successful"); - connectCard.onCommandDone(command); - QCOMPARE(context->getCardConnection(), cardConnection); + mState->onCommandDone(command); + QCOMPARE(mContext->getCardConnection(), cardConnection); QCOMPARE(spyContinue.count(), 1); workerThread.quit(); @@ -47,26 +76,28 @@ class test_StateConnectCard void test_OnReaderRemoved() { const QString readerName = QStringLiteral("name"); - const QSharedPointer context(new WorkflowContext()); - StateConnectCard connectCard(context); - QSignalSpy spy(&connectCard, &StateConnectCard::fireReaderRemoved); + QSignalSpy spy(mState.data(), &StateConnectCard::fireRetry); - connectCard.onReaderRemoved(readerName); + mState->onReaderRemoved(readerName); QCOMPARE(spy.count(), 0); - context->setReaderName(readerName); - connectCard.onReaderRemoved(readerName); + mContext->setReaderName(readerName); + mState->onReaderRemoved(readerName); QCOMPARE(spy.count(), 1); } - void test_OnAbort() + void test_OnEntry() { - const QSharedPointer context(new WorkflowContext()); - StateConnectCard connectCard(context); - QSignalSpy spyRetry(&connectCard, &StateConnectCard::fireRetry); - connectCard.onAbort(); + const QString stateName("name"); + mState->setStateName(stateName); + + QSignalSpy spyRetry(mState.data(), &StateConnectCard::fireRetry); + + mState->onEntry(nullptr); + + Q_EMIT mContext->fireReaderPlugInTypesChanged(); QCOMPARE(spyRetry.count(), 1); } diff --git a/test/qt/core/states/test_StateDestroyPace.cpp b/test/qt/core/states/test_StateDestroyPace.cpp index 9ee11ee..accb763 100644 --- a/test/qt/core/states/test_StateDestroyPace.cpp +++ b/test/qt/core/states/test_StateDestroyPace.cpp @@ -6,7 +6,7 @@ #include "MockCardConnectionWorker.h" -#include +#include using namespace governikus; diff --git a/test/qt/core/states/test_StateDidAuthenticateEac2.cpp b/test/qt/core/states/test_StateDidAuthenticateEac2.cpp new file mode 100644 index 0000000..eb174db --- /dev/null +++ b/test/qt/core/states/test_StateDidAuthenticateEac2.cpp @@ -0,0 +1,125 @@ +/*! + * \brief Unit test for \ref StateDidAuthenticateEac2 + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateDidAuthenticateEac2.h" + +#include "MockCardConnectionWorker.h" +#include "TestAuthContext.h" +#include "TestFileHelper.h" + +#include + +using namespace governikus; + +class MockDidAuthenticateEAC2Command + : public DidAuthenticateEAC2Command +{ + Q_OBJECT + + public: + explicit MockDidAuthenticateEAC2Command(const QSharedPointer& pCardConnectionWorker, const CVCertificateChain& pCvcChain, + const QString& pEphermalPublicKeyAsHex, const QString& pSignatureAsHex, const QByteArray& pAuthenticatedAuxiliaryDataAsBinary) + : DidAuthenticateEAC2Command(pCardConnectionWorker, pCvcChain, pEphermalPublicKeyAsHex, pSignatureAsHex, pAuthenticatedAuxiliaryDataAsBinary) + { + } + + + void setReturnCode(CardReturnCode pCode) + { + mReturnCode = pCode; + } + + +}; + + +class test_StateDidAuthenticateEac2 + : public QObject +{ + Q_OBJECT + QSharedPointer mAuthContext; + QSharedPointer mState; + QThread workerThread; + + private Q_SLOTS: + void init() + { + mAuthContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1.xml")); + mState.reset(new StateDidAuthenticateEac2(mAuthContext)); + workerThread.start(); + } + + + void cleanup() + { + mState.clear(); + mAuthContext.clear(); + workerThread.quit(); + workerThread.wait(); + } + + + void test_Run() + { + QSignalSpy spy(mState.data(), &StateDidAuthenticateEac2::fireAbort); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + const EstablishPaceChannelOutput invalidOutput; + + mAuthContext->setCardConnection(connection); + mAuthContext->setPaceOutputData(invalidOutput); + + mState->run(); + QCOMPARE(mState->mConnections.size(), 0); + QCOMPARE(mAuthContext->getStatus(), GlobalStatus::Code::Workflow_No_Permission_Error); + QCOMPARE(spy.count(), 1); + } + + + void test_OnCardCommandDone_data() + { + QTest::addColumn("code"); + QTest::addColumn("status"); + QTest::addColumn("abort"); + + QTest::newRow("ok") << CardReturnCode::OK << GlobalStatus::Code::No_Error << 0; + QTest::newRow("command_failed") << CardReturnCode::COMMAND_FAILED << GlobalStatus::Code::Workflow_Card_Removed << 1; + QTest::newRow("undefined") << CardReturnCode::UNDEFINED << GlobalStatus::Code::Workflow_No_Permission_Error << 1; + } + + + void test_OnCardCommandDone() + { + QFETCH(CardReturnCode, code); + QFETCH(GlobalStatus::Code, status); + QFETCH(int, abort); + + QSignalSpy spyAbort(mState.data(), &StateDidAuthenticateEac2::fireAbort); + QSignalSpy spyContinue(mState.data(), &StateDidAuthenticateEac2::fireContinue); + + const QSharedPointer worker(new MockCardConnectionWorker()); + const CVCertificateChain certificate; + const QString key("key"); + const QString signature("signature"); + const QByteArray auxiliaryData("data"); + const QSharedPointer response(new DIDAuthenticateResponseEAC2()); + const QSharedPointer command(new MockDidAuthenticateEAC2Command(worker, certificate, key, signature, auxiliaryData)); + + command->setReturnCode(code); + mAuthContext->setDidAuthenticateResponseEac2(response); + + mState->onCardCommandDone(command); + QCOMPARE(mAuthContext->getStatus(), status); + QCOMPARE(spyAbort.count(), abort); + QCOMPARE(spyContinue.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateDidAuthenticateEac2) +#include "test_StateDidAuthenticateEac2.moc" diff --git a/test/qt/core/states/test_StateEstablishPaceChannel.cpp b/test/qt/core/states/test_StateEstablishPaceChannel.cpp index 0fdab77..8910441 100644 --- a/test/qt/core/states/test_StateEstablishPaceChannel.cpp +++ b/test/qt/core/states/test_StateEstablishPaceChannel.cpp @@ -60,169 +60,169 @@ class test_StateEstablishPaceChannel Q_OBJECT QThread mWorkerThread; QSharedPointer mAuthContext; + QSharedPointer mState; private Q_SLOTS: void init() { mWorkerThread.start(); mAuthContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1.xml")); + mState.reset(new StateEstablishPaceChannel(mAuthContext)); } void cleanup() { + mState.clear(); mAuthContext.clear(); mWorkerThread.quit(); mWorkerThread.wait(); } + void test_Run_NoConnection() + { + QSignalSpy spyAbort(mState.data(), &StateEstablishPaceChannel::fireAbort); + + QTest::ignoreMessage(QtDebugMsg, "No card connection available."); + mState->run(); + QCOMPARE(spyAbort.count(), 1); + } + + + void test_Run() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&mWorkerThread); + const QSharedPointer connection(new CardConnection(worker)); + const QString password("0000000"); + mAuthContext->setPin(password); + mAuthContext->setCardConnection(connection); + mAuthContext->setEstablishPaceChannelType(PacePasswordId::PACE_PIN); + + QTest::ignoreMessage(QtDebugMsg, "Establish connection using PACE_PIN"); + mState->run(); + QCOMPARE(mAuthContext->getEstablishPaceChannelType(), PacePasswordId::PACE_PIN); + QCOMPARE(mState->mPasswordId, PacePasswordId::PACE_PIN); + } + + void test_OnUserCancelled() { - StateEstablishPaceChannel state(mAuthContext); const QSharedPointer worker(new MockCardConnectionWorker()); worker->moveToThread(&mWorkerThread); const QSharedPointer connection(new CardConnection(worker)); const CardInfo cInfo(CardType::NONE, QSharedPointer(), 3, false, false); ReaderInfo rInfo; rInfo.setCardInfo(cInfo); - connection->mReaderInfo = rInfo; + Q_EMIT worker->fireReaderInfoChanged(rInfo); mAuthContext->setCardConnection(connection); - QSignalSpy spyAbort(&state, &AbstractState::fireAbort); + QSignalSpy spyAbort(mState.data(), &AbstractState::fireAbort); QTest::ignoreMessage(QtInfoMsg, "Cancellation by user"); - state.onUserCancelled(); + mState->onUserCancelled(); QCOMPARE(mAuthContext->getStatus().getStatusCode(), GlobalStatus::Code::Workflow_Cancellation_By_User); QCOMPARE(mAuthContext->getLastPaceResult(), CardReturnCode::CANCELLATION_BY_USER); } - void test_OnEstablishConnectionDone_Pin() + void test_OnEstablishConnectionDone_data() { - StateEstablishPaceChannel state(mAuthContext); - state.mPasswordId = PacePasswordId::PACE_PIN; + QTest::addColumn("password"); + QTest::addColumn("retryCounter"); + QTest::addColumn("code"); + QTest::addColumn("result"); + QTest::addColumn("canAllowed"); + + QTest::newRow("PIN_OK") << PacePasswordId::PACE_PIN << 3 << CardReturnCode::OK << CardReturnCode::OK << false; + QTest::newRow("PIN_PUK_INOPERATIVE") << PacePasswordId::PACE_PIN << 1 << CardReturnCode::PUK_INOPERATIVE << CardReturnCode::PUK_INOPERATIVE << false; + QTest::newRow("PIN_CANCELLATION_BY_USER") << PacePasswordId::PACE_PIN << 2 << CardReturnCode::CANCELLATION_BY_USER << CardReturnCode::CANCELLATION_BY_USER << false; + QTest::newRow("PIN_INVALID_PIN_RETRY_COUNTER_3") << PacePasswordId::PACE_PIN << 3 << CardReturnCode::INVALID_PIN << CardReturnCode::INVALID_PIN << false; + QTest::newRow("PIN_INVALID_PIN_RETRY_COUNTER_2") << PacePasswordId::PACE_PIN << 2 << CardReturnCode::INVALID_PIN << CardReturnCode::INVALID_PIN_2 << false; + QTest::newRow("PIN_INVALID_PIN_RETRY_COUNTER_1") << PacePasswordId::PACE_PIN << 1 << CardReturnCode::INVALID_PIN << CardReturnCode::INVALID_PIN_3 << false; + QTest::newRow("CAN_OK_CAN_ALLOWED") << PacePasswordId::PACE_CAN << 3 << CardReturnCode::OK << CardReturnCode::OK << true; + QTest::newRow("CAN_OK") << PacePasswordId::PACE_CAN << 2 << CardReturnCode::OK << CardReturnCode::OK << false; + QTest::newRow("CAN_CANCELLATION_BY_USER") << PacePasswordId::PACE_CAN << 2 << CardReturnCode::CANCELLATION_BY_USER << CardReturnCode::CANCELLATION_BY_USER << true; + QTest::newRow("PUK_OK") << PacePasswordId::PACE_PUK << 0 << CardReturnCode::OK << CardReturnCode::OK_PUK << false; + QTest::newRow("PUK_INVALID_PIN_RETRY_COUNTER_1") << PacePasswordId::PACE_PUK << 0 << CardReturnCode::INVALID_PIN << CardReturnCode::INVALID_PIN << false; + } + + + void test_OnEstablishConnectionDone() + { + QFETCH(PacePasswordId, password); + QFETCH(int, retryCounter); + QFETCH(CardReturnCode, code); + QFETCH(CardReturnCode, result); + QFETCH(bool, canAllowed); + + QSignalSpy spyPaceChannelEstablished(mState.data(), &StateEstablishPaceChannel::firePaceChannelEstablished); + QSignalSpy spyPacePukEstablished(mState.data(), &StateEstablishPaceChannel::firePacePukEstablished); + QSignalSpy spyAbort(mState.data(), &StateEstablishPaceChannel::fireAbort); + QSignalSpy spyContinue(mState.data(), &StateEstablishPaceChannel::fireContinue); + + mState->mPasswordId = password; const QSharedPointer worker(new MockCardConnectionWorker()); const QSharedPointer command(new MockEstablishPaceChannelCommand(worker, PacePasswordId::PACE_PIN)); worker->moveToThread(&mWorkerThread); const QSharedPointer connection(new CardConnection(worker)); - const CardInfo cInfo(CardType::NONE, QSharedPointer(), 3, false, false); + const CardInfo cInfo(CardType::NONE, QSharedPointer(), retryCounter, false, false); ReaderInfo rInfo; rInfo.setCardInfo(cInfo); - connection->mReaderInfo = rInfo; + Q_EMIT worker->fireReaderInfoChanged(rInfo); mAuthContext->setCardConnection(connection); + mAuthContext->setCanAllowedMode(canAllowed); + command->setReturnCode(code); - QSignalSpy spyContinue(&state, &StateEstablishPaceChannel::fireContinue); - QSignalSpy spyPaceChannelEstablished(&state, &StateEstablishPaceChannel::firePaceChannelEstablished); - QSignalSpy spyAbort(&state, &StateEstablishPaceChannel::fireAbort); + if (code == CardReturnCode::OK && password == PacePasswordId::PACE_PIN) + { + QTest::ignoreMessage(QtDebugMsg, "PACE PIN succeeded. Setting expected retry counter to: 3"); + mState->onEstablishConnectionDone(command); + QCOMPARE(mAuthContext->getLastPaceResult(), result); + QCOMPARE(mAuthContext->getExpectedRetryCounter(), 3); + QCOMPARE(spyPaceChannelEstablished.count(), 1); + return; + } - command->setReturnCode(CardReturnCode::OK); - state.onEstablishConnectionDone(command); - QCOMPARE(mAuthContext->getLastPaceResult(), CardReturnCode::OK); - QCOMPARE(spyPaceChannelEstablished.count(), 1); + if (code == CardReturnCode::OK && password == PacePasswordId::PACE_CAN) + { + if (!canAllowed) + { + mState->onEstablishConnectionDone(command); + QCOMPARE(spyContinue.count(), 1); + return; + } + mState->onEstablishConnectionDone(command); + QCOMPARE(mAuthContext->getLastPaceResult(), result); + QCOMPARE(spyPaceChannelEstablished.count(), 1); + return; + } - mAuthContext->setStatus(GlobalStatus::Code::No_Error); - command->setReturnCode(CardReturnCode::CANCELLATION_BY_USER); - state.onEstablishConnectionDone(command); - QVERIFY(mAuthContext->getCardConnection()); - QCOMPARE(mAuthContext->getLastPaceResult(), CardReturnCode::CANCELLATION_BY_USER); - QCOMPARE(mAuthContext->getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); + if (code == CardReturnCode::OK && password == PacePasswordId::PACE_PUK) + { + QTest::ignoreMessage(QtDebugMsg, "PACE PUK succeeded. Resetting PACE passwords and setting expected retry counter to: -1"); + mState->onEstablishConnectionDone(command); + QCOMPARE(mAuthContext->getLastPaceResult(), result); + QCOMPARE(mAuthContext->getExpectedRetryCounter(), -1); + QCOMPARE(spyPacePukEstablished.count(), 1); + return; + } + + mState->onEstablishConnectionDone(command); + + if (code == CardReturnCode::PUK_INOPERATIVE) + { + QCOMPARE(mAuthContext->getStatus().getStatusCode(), GlobalStatus::Code::Card_Puk_Blocked); + } + + if (code == CardReturnCode::CANCELLATION_BY_USER) + { + QCOMPARE(mAuthContext->getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); + } + + QCOMPARE(mAuthContext->getLastPaceResult(), result); QCOMPARE(spyAbort.count(), 1); - - command->setReturnCode(CardReturnCode::INVALID_PIN); - mAuthContext->setCardConnection(connection); - state.onEstablishConnectionDone(command); - QCOMPARE(mAuthContext->getLastPaceResult(), CardReturnCode::INVALID_PIN); - QCOMPARE(spyAbort.count(), 2); - - QCOMPARE(spyContinue.count(), 0); - } - - - void test_OnEstablishConnectionDone_Can() - { - const QSharedPointer worker(new MockCardConnectionWorker()); - worker->moveToThread(&mWorkerThread); - const QSharedPointer connection(new CardConnection(worker)); - const CardInfo cardInfo(CardType::NONE, QSharedPointer(), 2, false, false); - const ReaderInfo readerInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo); - connection->onReaderInfoChanged(readerInfo); - const QSharedPointer context(new AuthContext(nullptr)); - context->setCardConnection(connection); - StateEstablishPaceChannel state(context); - state.mPasswordId = PacePasswordId::PACE_CAN; - QSharedPointer command(new MockEstablishPaceChannelCommand(worker, PacePasswordId::PACE_CAN)); - - QSignalSpy spyContinue(&state, &StateEstablishPaceChannel::fireContinue); - QSignalSpy spyAbort(&state, &StateEstablishPaceChannel::fireAbort); - - command->setReturnCode(CardReturnCode::OK); - state.onEstablishConnectionDone(command); - QCOMPARE(context->getLastPaceResult(), CardReturnCode::OK); - QCOMPARE(spyContinue.count(), 1); - - command->setReturnCode(CardReturnCode::CANCELLATION_BY_USER); - state.onEstablishConnectionDone(command); - QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); - QCOMPARE(context->getLastPaceResult(), CardReturnCode::CANCELLATION_BY_USER); - QCOMPARE(spyAbort.count(), 1); - - command->setReturnCode(CardReturnCode::INVALID_CAN); - state.onEstablishConnectionDone(command); - QCOMPARE(context->getLastPaceResult(), CardReturnCode::INVALID_CAN); - QCOMPARE(spyAbort.count(), 2); - } - - - void test_OnEstablishConnectionDone_Puk() - { - const QSharedPointer worker(new MockCardConnectionWorker()); - worker->moveToThread(&mWorkerThread); - const QSharedPointer connection(new CardConnection(worker)); - const CardInfo cInfo(CardType::UNKNOWN, QSharedPointer(), 3, false, false); - const ReaderInfo rInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cInfo); - connection->onReaderInfoChanged(rInfo); - const QSharedPointer context(new ChangePinContext()); - context->setCardConnection(connection); - const QSharedPointer command(new MockUnblockPinCommand(worker)); - - StateEstablishPaceChannel state(context); - state.mPasswordId = PacePasswordId::PACE_PUK; - QSignalSpy spyContinue(&state, &StateEstablishPaceChannel::fireContinue); - QSignalSpy spyPacePukEstablished(&state, &StateEstablishPaceChannel::firePacePukEstablished); - QSignalSpy spyAbort(&state, &StateEstablishPaceChannel::fireAbort); - - command->setReturnCode(CardReturnCode::OK); - state.onEstablishConnectionDone(command); - QCOMPARE(context->getLastPaceResult(), CardReturnCode::OK_PUK); - QCOMPARE(context->getSuccessMessage(), tr("PIN successfully unblocked")); - QCOMPARE(spyPacePukEstablished.count(), 1); - - command->setReturnCode(CardReturnCode::CANCELLATION_BY_USER); - state.onEstablishConnectionDone(command); - QCOMPARE(context->getLastPaceResult(), CardReturnCode::CANCELLATION_BY_USER); - QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Cancellation_By_User); - QCOMPARE(spyAbort.count(), 1); - - command->setReturnCode(CardReturnCode::INVALID_PUK); - state.onEstablishConnectionDone(command); - QCOMPARE(context->getLastPaceResult(), CardReturnCode::INVALID_PUK); - QCOMPARE(spyAbort.count(), 2); - - context->setStatus(GlobalStatus::Code::No_Error); - command->setReturnCode(CardReturnCode::PUK_INOPERATIVE); - state.onEstablishConnectionDone(command); - QCOMPARE(context->getLastPaceResult(), CardReturnCode::PUK_INOPERATIVE); - QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Puk_Blocked); - QCOMPARE(spyAbort.count(), 3); - - context->setStatus(GlobalStatus::Code::No_Error); - command->setReturnCode(CardReturnCode::UNKNOWN); - state.onEstablishConnectionDone(command); - QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::No_Error); - QCOMPARE(spyAbort.count(), 4); - - QCOMPARE(spyContinue.count(), 0); } diff --git a/test/qt/core/states/test_StateEstablishPaceChannelRemote.cpp b/test/qt/core/states/test_StateEstablishPaceChannelRemote.cpp index 87ad151..d5adc46 100644 --- a/test/qt/core/states/test_StateEstablishPaceChannelRemote.cpp +++ b/test/qt/core/states/test_StateEstablishPaceChannelRemote.cpp @@ -10,48 +10,168 @@ #include "MockCardConnectionWorker.h" #include "MockRemoteServer.h" -#include +#include using namespace governikus; +class MockEstablishPaceChannelCommand + : public EstablishPaceChannelCommand +{ + Q_OBJECT + + public: + explicit MockEstablishPaceChannelCommand(const QSharedPointer& pCardConnectionWorker) + : EstablishPaceChannelCommand(pCardConnectionWorker, PacePasswordId::PACE_PIN, QString("0000"), QByteArray(), QByteArray()) + { + EstablishPaceChannelOutput output; + output.setPaceReturnCode(CardReturnCode::OK); + mPaceOutput = output; + } + + + ~MockEstablishPaceChannelCommand() override = default; + + void setPaceReturnCode(CardReturnCode pCode) + { + EstablishPaceChannelOutput output; + output.setPaceReturnCode(pCode); + mPaceOutput = output; + } + + +}; + +class MockUnblockPinCommand + : public UnblockPinCommand +{ + Q_OBJECT + + public: + explicit MockUnblockPinCommand(const QSharedPointer& pCardConnectionWorker) + : UnblockPinCommand(pCardConnectionWorker, QString("0000")) + { + } + + + ~MockUnblockPinCommand() override = default; + + void setReturnCode(CardReturnCode pCode) + { + mReturnCode = pCode; + } + + +}; + class test_StateEstablishPaceChannelRemote : public QObject { Q_OBJECT + QSharedPointer mState; + QSharedPointer mContext; + QSharedPointer mWorker; + QThread mThread; private Q_SLOTS: - void initTestCase() + void init() { - Env::setCreator(std::function([&] { - return new MockRemoteServer(); - })); + mThread.start(); + mContext.reset(new RemoteServiceContext()); + mState.reset(new StateEstablishPaceChannelRemote(mContext)); + mWorker.reset(new MockCardConnectionWorker()); + } + + + void cleanup() + { + mState.clear(); + mContext.clear(); + mWorker.clear(); + mThread.quit(); + mThread.wait(); + } + + + void test_RunNoCardConnection() + { + const QSharedPointer message(new IfdEstablishPaceChannel("NFC Reader", "abc")); + mContext->setEstablishPaceChannelMessage(message); + QSignalSpy spy(mState.data(), &StateEstablishPaceChannelRemote::fireContinue); + + mState->run(); + QCOMPARE(spy.count(), 1); } void test_Run() { - QThread connectionThread; - connectionThread.start(); + const QSharedPointer message(new IfdEstablishPaceChannel("NFC Reader", "input")); + mContext->setEstablishPaceChannelMessage(message); + mWorker->moveToThread(&mThread); + const QSharedPointer connection(new CardConnection(mWorker)); + mContext->setCardConnection(connection); + mContext->setPin("0000"); - const QSharedPointer context(new RemoteServiceContext()); - StateEstablishPaceChannelRemote state(context); - const QString slotHandle = QStringLiteral("slothandle"); - const QByteArray input("abc"); - const QSharedPointer message(new IfdEstablishPaceChannel(slotHandle, input)); - context->setEstablishPaceChannelMessage(message); - const QSharedPointer worker(new MockCardConnectionWorker()); - worker->moveToThread(&connectionThread); - const QSharedPointer connection(new CardConnection(worker)); - context->setCardConnection(connection); - context->setPin(QString("1234")); + mState->run(); + QCOMPARE(mState->mConnections.size(), 1); + } - state.run(); - QCOMPARE(context->getPin(), QString("1234")); - QCOMPARE(state.mConnections.size(), 1); - connectionThread.quit(); - connectionThread.wait(); + void test_OnReaderInfoChanged() + { + ReaderInfo info; + QSignalSpy spy(mState.data(), &StateEstablishPaceChannelRemote::fireContinue); + + mState->onReaderInfoChanged(info); + QCOMPARE(mContext->getEstablishPaceChannelOutput().getPaceReturnCode(), CardReturnCode::CARD_NOT_FOUND); + QCOMPARE(spy.count(), 1); + } + + + void test_OnEstablishConnectionDoneEstablishPaceChannelCommand() + { + QSignalSpy spy(mState.data(), &StateEstablishPaceChannelRemote::fireContinue); + const QSharedPointer command(new MockEstablishPaceChannelCommand(mWorker)); + mContext->setExpectedRetryCounter(2); + + mState->mPasswordId = PacePasswordId::PACE_PIN; + QTest::ignoreMessage(QtDebugMsg, "Correct PACE password. Expected retry counter is now 3"); + mState->onEstablishConnectionDone(command); + QCOMPARE(mContext->getExpectedRetryCounter(), 3); + + mContext->setExpectedRetryCounter(2); + command->setPaceReturnCode(CardReturnCode::INVALID_PIN); + QTest::ignoreMessage(QtDebugMsg, "Wrong PACE password. Decreasing expected retry counter to 1"); + mState->onEstablishConnectionDone(command); + QCOMPARE(mContext->getExpectedRetryCounter(), 1); + + QCOMPARE(spy.count(), 2); + } + + + void test_OnEstablishConnectionDoneUnblockPinCommand() + { + QSignalSpy spy(mState.data(), &StateEstablishPaceChannelRemote::fireContinue); + const QSharedPointer command(new MockUnblockPinCommand(mWorker)); + + command->setReturnCode(CardReturnCode::OK); + QTest::ignoreMessage(QtDebugMsg, "Resetting PACE passwords and setting expected retry counter to -1"); + mState->onEstablishConnectionDone(command); + QCOMPARE(mContext->getExpectedRetryCounter(), -1); + QCOMPARE(mContext->getNewPin(), QString()); + + command->setReturnCode(CardReturnCode::INVALID_PUK); + QTest::ignoreMessage(QtDebugMsg, "Resetting PACE passwords and setting expected retry counter to -1"); + mState->onEstablishConnectionDone(command); + QCOMPARE(mContext->getExpectedRetryCounter(), -1); + QCOMPARE(mContext->getNewPin(), QString()); + + command->setReturnCode(CardReturnCode::UNDEFINED); + mState->onEstablishConnectionDone(command); + QCOMPARE(mContext->getEstablishPaceChannelOutput().getPaceReturnCode(), CardReturnCode::UNDEFINED); + + QCOMPARE(spy.count(), 3); } diff --git a/test/qt/core/states/test_StateEstablishPaceChannelResponse.cpp b/test/qt/core/states/test_StateEstablishPaceChannelResponse.cpp new file mode 100644 index 0000000..7b5dd22 --- /dev/null +++ b/test/qt/core/states/test_StateEstablishPaceChannelResponse.cpp @@ -0,0 +1,137 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "states/remote_service/StateEstablishPaceChannelResponse.h" + +#include "states/StateBuilder.h" + +#include "MockDataChannel.h" +#include "MockRemoteServer.h" + +#include + +using namespace governikus; + +class MockServerMsgHandler + : public ServerMessageHandler +{ + Q_OBJECT + QString mResponse; + + public: + explicit MockServerMsgHandler() + : ServerMessageHandler() + , mResponse() + { + } + + + virtual void sendEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPaceChannelOutput& pChannelOutput) override + { + Q_UNUSED(pChannelOutput); + mResponse = pSlotHandle; + } + + + virtual void sendModifyPinResponse(const QString& pSlotHandle, const ResponseApdu& pResponseApdu) override + { + Q_UNUSED(pSlotHandle); + Q_UNUSED(pResponseApdu); + } + + + QString getResponse() + { + return mResponse; + } + + +}; + +class test_StateEstablishPaceChannelResponse + : public QObject +{ + Q_OBJECT + QSharedPointer mState; + QSharedPointer mContext; + + private Q_SLOTS: + void initTestCase() + { + Env::setCreator(std::function([&] { + return new MockRemoteServer(); + })); + } + + + void init() + { + mContext.reset(new RemoteServiceContext()); + mState.reset(StateBuilder::createState(mContext)); + } + + + void cleanup() + { + mContext.clear(); + mState.clear(); + } + + + void test_Run_data() + { + QTest::addColumn("hexBytes"); + QTest::addColumn("wrongPacePasswordCounter"); + QTest::addColumn("continueCounter"); + + QByteArray data1 = QByteArray("30 10" + "a1 06 04 04 f00663c2" + "a2 04 04 02 0000" + "a3 00" + "9000"); + QByteArray data2 = QByteArray("30 5A" + "A1 06 04 04 F0200001" + "A2 04 04 02 9000" + "A3 02 31 00" + "A4 22 04 20 24e41d62b8c848226b86fcc6c7657577dca47ad2bf21573617bae84807f85c6b" + "A5 10 04 0e 4445435643416549443030313033" + "A6 10 04 0e 4445435643416549443030313032" + "9000"); + + QTest::newRow("wrongPacePassword") << data1 << 1 << 0; + QTest::newRow("correctPacePassword") << data2 << 0 << 1; + } + + + void test_Run() + { + QFETCH(QByteArray, hexBytes); + QFETCH(int, wrongPacePasswordCounter); + QFETCH(int, continueCounter); + + QSignalSpy spyWrongPacePassword(mState.data(), &StateEstablishPaceChannelResponse::fireWrongPacePassword); + QSignalSpy spyContinue(mState.data(), &StateEstablishPaceChannelResponse::fireContinue); + + const QString slotHandle("slot handle"); + const QSharedPointer msg(new IfdEstablishPaceChannel(slotHandle)); + mContext->setEstablishPaceChannelMessage(msg); + EstablishPaceChannelOutput channelOutput; + channelOutput.parseFromCcid(QByteArray::fromHex(hexBytes), PacePasswordId::PACE_PIN); + mContext->setEstablishPaceChannelOutput(channelOutput); + const QSharedPointer msgHandler(new MockServerMsgHandler()); + auto server = mContext->getRemoteServer().staticCast(); + server->setMessageHandler(msgHandler); + + mContext->setStateApproved(); + mState->onStateApprovedChanged(); + QCOMPARE(spyWrongPacePassword.count(), wrongPacePasswordCounter); + QCOMPARE(spyContinue.count(), continueCounter); + QCOMPARE(msgHandler->getResponse(), slotHandle); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateEstablishPaceChannelResponse) +#include "test_StateEstablishPaceChannelResponse.moc" diff --git a/test/qt/core/states/test_StateExtractCvcsFromEac1InputType.cpp b/test/qt/core/states/test_StateExtractCvcsFromEac1InputType.cpp index 5777492..9a317d6 100644 --- a/test/qt/core/states/test_StateExtractCvcsFromEac1InputType.cpp +++ b/test/qt/core/states/test_StateExtractCvcsFromEac1InputType.cpp @@ -6,6 +6,8 @@ #include "states/StateExtractCvcsFromEac1InputType.h" +#include "states/StateBuilder.h" + #include "TestAuthContext.h" #include "TestFileHelper.h" @@ -22,7 +24,7 @@ class test_StateExtractCvcsFromEac1InputType QVector > mTerminalCvcs, mDvCvcs, mLinkCvcs, mCvcas; QScopedPointer mState; - QSharedPointer mAuthContext; + QSharedPointer mAuthContext; Q_SIGNALS: void fireStateStart(QEvent* pEvent); @@ -44,10 +46,10 @@ class test_StateExtractCvcsFromEac1InputType { mAuthContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1_3.xml")); - mState.reset(new StateExtractCvcsFromEac1InputType(mAuthContext)); + mState.reset(StateBuilder::createState(mAuthContext)); mState->setStateName("StateExtractCvcsFromEac1InputType"); - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.clear(); + mAuthContext->clearCvCertificates(); connect(this, &test_StateExtractCvcsFromEac1InputType::fireStateStart, mState.data(), &AbstractState::onEntry, Qt::ConnectionType::DirectConnection); } @@ -61,7 +63,7 @@ class test_StateExtractCvcsFromEac1InputType void testNoDvCvc() { - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mTerminalCvcs.at(0)); + mAuthContext->addCvCertificate(mTerminalCvcs.at(0)); QSignalSpy spy(mState.data(), &StateExtractCvcsFromEac1InputType::fireAbort); Q_EMIT fireStateStart(nullptr); @@ -73,9 +75,9 @@ class test_StateExtractCvcsFromEac1InputType void testMoreThanOneDvCvc() { - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mTerminalCvcs.at(0)); - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mDvCvcs.at(0)); - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mDvCvcs.at(1)); + mAuthContext->addCvCertificate(mTerminalCvcs.at(0)); + mAuthContext->addCvCertificate(mDvCvcs.at(0)); + mAuthContext->addCvCertificate(mDvCvcs.at(1)); QSignalSpy spy(mState.data(), &StateExtractCvcsFromEac1InputType::fireAbort); Q_EMIT fireStateStart(nullptr); @@ -87,7 +89,7 @@ class test_StateExtractCvcsFromEac1InputType void testNoTerminalCvc() { - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mDvCvcs.at(0)); + mAuthContext->addCvCertificate(mDvCvcs.at(0)); QSignalSpy spy(mState.data(), &StateExtractCvcsFromEac1InputType::fireAbort); Q_EMIT fireStateStart(nullptr); @@ -99,9 +101,9 @@ class test_StateExtractCvcsFromEac1InputType void testMoreThanOneTerminalCvc() { - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mDvCvcs.at(0)); - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mTerminalCvcs.at(0)); - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mTerminalCvcs.at(1)); + mAuthContext->addCvCertificate(mDvCvcs.at(0)); + mAuthContext->addCvCertificate(mTerminalCvcs.at(0)); + mAuthContext->addCvCertificate(mTerminalCvcs.at(1)); QSignalSpy spy(mState.data(), &StateExtractCvcsFromEac1InputType::fireAbort); Q_EMIT fireStateStart(nullptr); @@ -113,8 +115,8 @@ class test_StateExtractCvcsFromEac1InputType void testSuccess() { - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mDvCvcs.at(0)); - mAuthContext->getDidAuthenticateEac1()->mEac1InputType.mCvCertificates.append(mTerminalCvcs.at(0)); + mAuthContext->addCvCertificate(mDvCvcs.at(0)); + mAuthContext->addCvCertificate(mTerminalCvcs.at(0)); QSignalSpy spy(mState.data(), &StateExtractCvcsFromEac1InputType::fireContinue); Q_EMIT fireStateStart(nullptr); diff --git a/test/qt/core/states/test_StateGenericSendReceive.cpp b/test/qt/core/states/test_StateGenericSendReceive.cpp index 70d3b24..69c790a 100644 --- a/test/qt/core/states/test_StateGenericSendReceive.cpp +++ b/test/qt/core/states/test_StateGenericSendReceive.cpp @@ -123,19 +123,25 @@ class test_StateGenericSendReceive void onPreSharedKeyAuthenticationRequired() { - const QByteArray data("data"); - const QByteArray psk("psk"); - const QByteArray sessionIdentifier("session"); + const QByteArray data("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + ""); const QSharedPointer token(new TcToken(data)); - token->mPsk = psk; - token->mSessionIdentifier = sessionIdentifier; mAuthContext->setTcToken(token); auto* authenticator = new QSslPreSharedKeyAuthenticator(); QTest::ignoreMessage(QtDebugMsg, "pre-shared key authentication requested: \"\""); mState->onPreSharedKeyAuthenticationRequired(authenticator); - QCOMPARE(authenticator->identity(), sessionIdentifier); - QCOMPARE(authenticator->preSharedKey(), QByteArray::fromHex(psk)); + QCOMPARE(authenticator->identity(), QByteArray("1A2BB129")); + QCOMPARE(authenticator->preSharedKey(), QByteArray::fromHex("4BC1A0B5")); } @@ -215,7 +221,7 @@ class test_StateGenericSendReceive << GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Certificate_Unsupported_Algorithm_Or_Length << GlobalStatus::Code::Workflow_TrustedChannel_TimeOut << GlobalStatus::Code::Workflow_TrustedChannel_Proxy_Error - << GlobalStatus::Code::Workflow_TrustedChannel_Ssl_Establishment_Error + << GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error << GlobalStatus::Code::Workflow_TrustedChannel_Server_Format_Error << GlobalStatus::Code::Workflow_TrustedChannel_Other_Network_Error; diff --git a/test/qt/core/states/test_StateGetSelfAuthenticationData.cpp b/test/qt/core/states/test_StateGetSelfAuthenticationData.cpp new file mode 100644 index 0000000..7acd4f7 --- /dev/null +++ b/test/qt/core/states/test_StateGetSelfAuthenticationData.cpp @@ -0,0 +1,147 @@ +/*! + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateGetSelfAuthenticationData.h" + +#include "HttpServer.h" +#include "LogHandler.h" + +#include "MockNetworkReply.h" +#include "TestAuthContext.h" +#include "TestFileHelper.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(network) + +using namespace governikus; + +class test_StateGetSelfAuthenticationData + : public QObject +{ + Q_OBJECT + QSharedPointer mState; + QSharedPointer mContext; + + private Q_SLOTS: + void init() + { + Env::getSingleton()->init(); + mContext.reset(new SelfAuthContext()); + mState.reset(new StateGetSelfAuthenticationData(mContext)); + } + + + void cleanup() + { + mState.clear(); + mContext.clear(); + } + + + void test_ReportCommunicationError() + { + MockNetworkReply reply; + mState->mReply = QPointer(&reply); + + QSignalSpy spy(mState.data(), &StateGetSelfAuthenticationData::fireAbort); + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + mState->reportCommunicationError(GlobalStatus::Code::Unknown_Error); + QVERIFY(logSpy.takeLast().at(0).toString().contains("Operation aborted")); + QVERIFY(logSpy.takeLast().at(0).toString().contains("Unknown_Error | \"An unexpected error has occurred during processing.\"")); + QCOMPARE(mContext->getStatus(), GlobalStatus::Code::Unknown_Error); + QCOMPARE(spy.count(), 1); + + mContext->setStatus(GlobalStatus::Code::No_Error); + + mState->reportCommunicationError(GlobalStatus::Code::Card_Not_Found); + QVERIFY(logSpy.takeLast().at(0).toString().contains("Operation aborted")); + QVERIFY(logSpy.takeLast().at(0).toString().contains("Card_Not_Found | \"Card does not exist\"")); + QCOMPARE(mContext->getStatus(), GlobalStatus::Code::Card_Not_Found); + QCOMPARE(spy.count(), 2); + } + + + void test_OnSslHandshakeDone() + { + MockNetworkReply reply; + mState->mReply = QPointer(&reply); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + + mState->onSslHandshakeDone(); + QVERIFY(logSpy.takeLast().at(0).toString().contains("Operation aborted")); + + reply.setSslConfiguration(QSslConfiguration()); + mState->onSslHandshakeDone(); + const QString logMsg(logSpy.at(0).at(0).toString()); + QVERIFY(logMsg.contains("Used session cipher")); + } + + + void test_OnSslErrors() + { + MockNetworkReply reply; + mState->mReply = QPointer(&reply); + QSignalSpy spy(mState.data(), &StateGetSelfAuthenticationData::fireAbort); + + mState->onSslErrors(QList()); + QCOMPARE(spy.count(), 0); + + const QList errorList = {QSslError::CertificateNotYetValid, QSslError::CertificateUntrusted}; + mState->onSslErrors(errorList); + QCOMPARE(spy.count(), 1); + } + + + void test_OnNetworkReplyNoValidData() + { + MockNetworkReply emptyReply; + mState->mReply = QPointer(&emptyReply); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + QSignalSpy spyAbort(mState.data(), &StateGetSelfAuthenticationData::fireAbort); + + mState->onNetworkReply(); + const QString logMsg(logSpy.takeLast().at(0).toString()); + QVERIFY(logMsg.contains("No valid data of self-authentication.")); + QCOMPARE(spyAbort.count(), 1); + QCOMPARE(mState->getContext()->getStatus(), GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided); + } + + + void test_OnNetworkReplyValidData() + { + const auto& data = TestFileHelper::readFile(":/self/SelfAuthenticationData.json"); + MockNetworkReply reply(data); + mState->mReply = QPointer(&reply); + QSignalSpy spyContinue(mState.data(), &StateGetSelfAuthenticationData::fireContinue); + + mState->onNetworkReply(); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_OnNetworkReplyWrongHttpStatus() + { + MockNetworkReply reply; + reply.setAttribute(QNetworkRequest::Attribute::HttpStatusCodeAttribute, 500); + mState->mReply = QPointer(&reply); + + QSignalSpy logSpy(Env::getSingleton(), &LogHandler::fireLog); + QSignalSpy spyAbort(mState.data(), &StateGetSelfAuthenticationData::fireAbort); + + mState->onNetworkReply(); + const QString logMsg(logSpy.takeLast().at(0).toString()); + QVERIFY(logMsg.contains("Could not read data for self-authentication.")); + QCOMPARE(spyAbort.count(), 1); + QCOMPARE(mState->getContext()->getStatus(), GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateGetSelfAuthenticationData) +#include "test_StateGetSelfAuthenticationData.moc" diff --git a/test/qt/core/states/test_StateInitializeFramework.cpp b/test/qt/core/states/test_StateInitializeFramework.cpp index 75267de..fd7fd51 100644 --- a/test/qt/core/states/test_StateInitializeFramework.cpp +++ b/test/qt/core/states/test_StateInitializeFramework.cpp @@ -6,8 +6,8 @@ #include "states/StateInitializeFramework.h" #include "TestFileHelper.h" -#include -#include +#include +#include #include diff --git a/test/qt/core/states/test_StateMaintainCardConnection.cpp b/test/qt/core/states/test_StateMaintainCardConnection.cpp new file mode 100644 index 0000000..b7815ce --- /dev/null +++ b/test/qt/core/states/test_StateMaintainCardConnection.cpp @@ -0,0 +1,200 @@ +/*! + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateMaintainCardConnection.h" + +#include "MockCardConnectionWorker.h" + +#include + +using namespace governikus; + +class test_StateMaintainCardConnection + : public QObject +{ + Q_OBJECT + QSharedPointer mState; + QSharedPointer mContext; + QThread workerThread; + + private Q_SLOTS: + void init() + { + workerThread.start(); + mContext.reset(new WorkflowContext()); + mState.reset(new StateMaintainCardConnection(mContext)); + } + + + void cleanup() + { + mContext.clear(); + mState.reset(); + workerThread.quit(); + workerThread.wait(); + } + + + void test_Run_Error() + { + QSignalSpy spy(mState.data(), &StateMaintainCardConnection::fireAbort); + + mContext->setStatus(GlobalStatus::Code::Unknown_Error); + mState->run(); + QCOMPARE(spy.count(), 1); + } + + + void test_Run_ResultUnrecoverable_data() + { + QTest::addColumn("code"); + QTest::addColumn("status"); + + QTest::newRow("cancellation_by_user") << CardReturnCode::CANCELLATION_BY_USER << GlobalStatus::Code::Card_Cancellation_By_User; + QTest::newRow("puk_inoperative") << CardReturnCode::PUK_INOPERATIVE << GlobalStatus::Code::Card_Puk_Blocked; + QTest::newRow("input_timeout") << CardReturnCode::INPUT_TIME_OUT << GlobalStatus::Code::Card_Input_TimeOut; + } + + + void test_Run_ResultUnrecoverable() + { + QFETCH(CardReturnCode, code); + QFETCH(GlobalStatus::Code, status); + QSignalSpy spy(mState.data(), &StateMaintainCardConnection::fireAbort); + + mContext->setLastPaceResult(code); + QTest::ignoreMessage(QtDebugMsg, "Last PACE result is unrecoverable. Aborting."); + mState->run(); + QCOMPARE(mContext->getStatus(), status); + QCOMPARE(spy.count(), 1); + } + + + void test_Run_WrongPacePassword_data() + { + QTest::addColumn("code"); + + QTest::newRow("invalid_can") << CardReturnCode::INVALID_CAN; + QTest::newRow("invalid_pin") << CardReturnCode::INVALID_PIN; + QTest::newRow("invalid_pin_2") << CardReturnCode::INVALID_PIN_2; + QTest::newRow("invalid_pin_3") << CardReturnCode::INVALID_PIN_3; + QTest::newRow("invalid_puk") << CardReturnCode::INVALID_PUK; + QTest::newRow("new_pin_mismatch") << CardReturnCode::NEW_PIN_MISMATCH; + QTest::newRow("new_pin_invalid_length") << CardReturnCode::NEW_PIN_INVALID_LENGTH; + QTest::newRow("pin_not_blocked") << CardReturnCode::PIN_NOT_BLOCKED; + QTest::newRow("pin_blocked") << CardReturnCode::PIN_BLOCKED; + } + + + void test_Run_WrongPacePassword() + { + QFETCH(CardReturnCode, code); + + QSignalSpy spyNoCardConnection(mState.data(), &StateMaintainCardConnection::fireNoCardConnection); + QSignalSpy spyUpdateRetryCounter(mState.data(), &StateMaintainCardConnection::fireForceUpdateRetryCounter); + + const QString password("111111"); + mContext->setPin(password); + mContext->setCan(password); + mContext->setPuk(password); + mContext->setLastPaceResult(code); + + QTest::ignoreMessage(QtDebugMsg, "Reseting all PACE passwords."); + QTest::ignoreMessage(QtDebugMsg, "No card connection available."); + mState->run(); + QCOMPARE(spyNoCardConnection.count(), 1); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + mContext->setCardConnection(connection); + + QTest::ignoreMessage(QtDebugMsg, "Reseting all PACE passwords."); + QTest::ignoreMessage(QtDebugMsg, "Trigger retry counter update."); + mState->run(); + QCOMPARE(spyUpdateRetryCounter.count(), 1); + + QCOMPARE(mContext->getPin(), QString()); + QCOMPARE(mContext->getCan(), QString()); + QCOMPARE(mContext->getPuk(), QString()); + } + + + void test_Run_ResetCardConnectionAndLastPaceResult_data() + { + QTest::addColumn("code"); + QTest::addColumn("doReset"); + + QTest::newRow("card_not_found") << CardReturnCode::CARD_NOT_FOUND << true; + QTest::newRow("retry_allowed") << CardReturnCode::RETRY_ALLOWED << true; + + QTest::newRow("undefined") << CardReturnCode::UNDEFINED << false; + QTest::newRow("unknown") << CardReturnCode::UNKNOWN << false; + QTest::newRow("command_failed") << CardReturnCode::COMMAND_FAILED << false; + QTest::newRow("protocol_error") << CardReturnCode::PROTOCOL_ERROR << false; + QTest::newRow("unexpectd_transmit_status") << CardReturnCode::UNEXPECTED_TRANSMIT_STATUS << false; + } + + + void test_Run_ResetCardConnectionAndLastPaceResult() + { + QFETCH(CardReturnCode, code); + QFETCH(bool, doReset); + QSignalSpy spyNoCard(mState.data(), &StateMaintainCardConnection::fireNoCardConnection); + QSignalSpy spyAbort(mState.data(), &StateMaintainCardConnection::fireAbort); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + mContext->setCardConnection(connection); + mContext->setLastPaceResult(code); + if (doReset) + { + QTest::ignoreMessage(QtDebugMsg, "No card connection available."); + } + else + { + QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Last PACE result: .*")); + QTest::ignoreMessage(QtDebugMsg, "Last PACE result is unrecoverable. Aborting."); + } + mState->run(); + QCOMPARE(mContext->getCardConnection(), doReset ? QSharedPointer() : connection); + QCOMPARE(mContext->getLastPaceResult(), doReset ? CardReturnCode::OK : code); + QCOMPARE(spyNoCard.count(), doReset ? 1 : 0); + QCOMPARE(spyAbort.count(), doReset ? 0 : 1); + } + + + void test_Run_Ok() + { + QSignalSpy spyNoCardConnection(mState.data(), &StateMaintainCardConnection::fireNoCardConnection); + QSignalSpy spyContinue(mState.data(), &StateMaintainCardConnection::fireContinue); + QSignalSpy spyUpdateRetryCounter(mState.data(), &StateMaintainCardConnection::fireForceUpdateRetryCounter); + + mContext->setLastPaceResult(CardReturnCode::OK); + QTest::ignoreMessage(QtDebugMsg, "No card connection available."); + mState->run(); + QCOMPARE(spyNoCardConnection.count(), 1); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + mContext->setCardConnection(connection); + + QTest::ignoreMessage(QtDebugMsg, "Card connection is fine. Proceeding."); + mState->run(); + QCOMPARE(spyContinue.count(), 1); + + mContext->setLastPaceResult(CardReturnCode::OK_PUK); + QTest::ignoreMessage(QtDebugMsg, "PIN unblocked! Triggering retry counter update."); + mState->run(); + QCOMPARE(spyUpdateRetryCounter.count(), 1); + QCOMPARE(mContext->getLastPaceResult(), CardReturnCode::OK); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateMaintainCardConnection) +#include "test_StateMaintainCardConnection.moc" diff --git a/test/qt/core/states/test_StatePreVerification.cpp b/test/qt/core/states/test_StatePreVerification.cpp index 2b29cfb..ba37008 100644 --- a/test/qt/core/states/test_StatePreVerification.cpp +++ b/test/qt/core/states/test_StatePreVerification.cpp @@ -75,7 +75,7 @@ class test_StatePreVerification void testSignatureInvalid() { const_cast(&mState->mValidationDateTime)->setDate(QDate(2013, 12, 1)); - auto signature = mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.at(0)->getEcdsaSignature(); + auto signature = mAuthContext->getDidAuthenticateEac1()->getCvCertificates().at(0)->getEcdsaSignature(); #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) BIGNUM* signaturePart = signature->r; #else @@ -139,13 +139,13 @@ class test_StatePreVerification } settings.save(); - const int expectedCvcaSize = 12; + const int expectedCvcaSize = 13; QCOMPARE(mState->mTrustedCvcas.size(), expectedCvcaSize); const_cast(&mState->mValidationDateTime)->setDate(QDate(2013, 12, 1)); auto& trustedCvcas = const_cast >&>(mState->mTrustedCvcas); - remove(trustedCvcas, mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.at(3)); - remove(trustedCvcas, mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.at(2)); + remove(trustedCvcas, mAuthContext->getDidAuthenticateEac1()->getCvCertificates().at(3)); + remove(trustedCvcas, mAuthContext->getDidAuthenticateEac1()->getCvCertificates().at(2)); QCOMPARE(mState->mTrustedCvcas.size(), expectedCvcaSize - 2); diff --git a/test/qt/core/states/test_StatePreparePace.cpp b/test/qt/core/states/test_StatePreparePace.cpp new file mode 100644 index 0000000..a5675b5 --- /dev/null +++ b/test/qt/core/states/test_StatePreparePace.cpp @@ -0,0 +1,155 @@ +/*! + * \brief Unit tests for \ref StatePreparePace + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StatePreparePace.h" + +#include "states/StateBuilder.h" + +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + +class test_StatePreparePace + : public QObject +{ + Q_OBJECT + QSharedPointer mState; + QSharedPointer mContext; + QThread workerThread; + + private Q_SLOTS: + void init() + { + mContext.reset(new WorkflowContext()); + mState.reset(StateBuilder::createState(mContext)); + workerThread.start(); + mState->onEntry(nullptr); + } + + + void cleanup() + { + workerThread.quit(); + workerThread.wait(); + mContext.clear(); + mState.clear(); + } + + + void test_Run_NoCardConnection() + { + QSignalSpy spyAbort(mState.data(), &StatePreparePace::fireAbort); + + QTest::ignoreMessage(QtDebugMsg, "Card connection lost."); + mContext->setStateApproved(); + QCOMPARE(spyAbort.count(), 1); + QCOMPARE(mContext->getEstablishPaceChannelType(), PacePasswordId::UNKNOWN); + } + + + void test_Run_CanAllowed() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + mContext->setCardConnection(connection); + mContext->setCanAllowedMode(true); + QTest::ignoreMessage(QtDebugMsg, "CAN allowed required"); + mContext->setStateApproved(); + QCOMPARE(mContext->getEstablishPaceChannelType(), PacePasswordId::PACE_CAN); + } + + + void test_Run_RetryCounter0() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 0); + const ReaderInfo readerInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo); + mContext->setCardConnection(connection); + Q_EMIT worker->fireReaderInfoChanged(readerInfo); + QSignalSpy spyEnterPacePassword(mState.data(), &StatePreparePace::fireEnterPacePassword); + QSignalSpy spyEstablishPaceChannel(mState.data(), &StatePreparePace::fireEstablishPaceChannel); + + QTest::ignoreMessage(QtDebugMsg, "PUK required"); + mContext->setStateApproved(); + QCOMPARE(spyEnterPacePassword.count(), 1); + QCOMPARE(mContext->getEstablishPaceChannelType(), PacePasswordId::PACE_PUK); + + mContext->setStateApproved(false); + + const QString puk("0987654321"); + mContext->setPuk(puk); + mContext->setStateApproved(); + QCOMPARE(spyEstablishPaceChannel.count(), 1); + } + + + void test_Run_RetryCounter1() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 1); + const ReaderInfo readerInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo); + QSignalSpy spyEnterPacePassword(mState.data(), &StatePreparePace::fireEnterPacePassword); + QSignalSpy spyEstablishPaceChannel(mState.data(), &StatePreparePace::fireEstablishPaceChannel); + mContext->setCardConnection(connection); + Q_EMIT worker->fireReaderInfoChanged(readerInfo); + + QTest::ignoreMessage(QtDebugMsg, "CAN required"); + QTest::ignoreMessage(QtDebugMsg, "PACE CAN done: false"); + mContext->setStateApproved(); + + mContext->setStateApproved(false); + + const QString can("000000"); + mContext->setCan(can); + QTest::ignoreMessage(QtDebugMsg, "CAN required"); + QTest::ignoreMessage(QtDebugMsg, "PACE CAN done: false"); + mContext->setStateApproved(); + QCOMPARE(spyEstablishPaceChannel.count(), 1); + } + + + void test_Run_RetryCounter3() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 3); + const ReaderInfo readerInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo); + mContext->setCardConnection(connection); + Q_EMIT worker->fireReaderInfoChanged(readerInfo); + QSignalSpy spyEnterPacePassword(mState.data(), &StatePreparePace::fireEnterPacePassword); + QSignalSpy spyEstablishPaceChannel(mState.data(), &StatePreparePace::fireEstablishPaceChannel); + + QTest::ignoreMessage(QtDebugMsg, "PIN allowed"); + QTest::ignoreMessage(QtDebugMsg, "PACE PIN done: false"); + mContext->setStateApproved(); + QCOMPARE(spyEnterPacePassword.count(), 1); + QCOMPARE(spyEstablishPaceChannel.count(), 0); + QCOMPARE(mContext->getEstablishPaceChannelType(), PacePasswordId::PACE_PIN); + + mContext->setStateApproved(false); + + const QString pin("000000"); + mContext->setPin(pin); + mContext->setStateApproved(); + QCOMPARE(spyEnterPacePassword.count(), 1); + QCOMPARE(spyEstablishPaceChannel.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StatePreparePace) +#include "test_StatePreparePace.moc" diff --git a/test/qt/core/states/test_StatePreparePaceRemote.cpp b/test/qt/core/states/test_StatePreparePaceRemote.cpp new file mode 100644 index 0000000..52fc12d --- /dev/null +++ b/test/qt/core/states/test_StatePreparePaceRemote.cpp @@ -0,0 +1,127 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "states/remote_service/StatePreparePaceRemote.h" + +#include "AppSettings.h" +#include "EstablishPaceChannelParser.h" +#include "states/StateBuilder.h" + +#include + + +using namespace governikus; + +class test_StatePreparePaceRemote + : public QObject +{ + Q_OBJECT + QSharedPointer mContext; + QSharedPointer mState; + + static QSharedPointer createMessage(PacePasswordId pId) + { + EstablishPaceChannel builder; + builder.setPasswordId(pId); + const auto command = builder.createCommandDataCcid(); + const auto& buffer = command.getBuffer(); + return QSharedPointer(new IfdEstablishPaceChannel("slot", buffer)); + } + + + private Q_SLOTS: + void init() + { + mContext.reset(new RemoteServiceContext()); + mState.reset(StateBuilder::createState(mContext)); + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(true); + + mState->onEntry(nullptr); + } + + + void cleanup() + { + mContext.clear(); + mState.clear(); + } + + + void test_RunPin() + { + const auto& message = createMessage(PacePasswordId::PACE_PIN); + mContext->setEstablishPaceChannelMessage(message); + QSignalSpy spyEnterPacePassword(mState.data(), &StatePreparePaceRemote::fireEnterPacePassword); + QSignalSpy spyContinue(mState.data(), &StatePreparePaceRemote::fireContinue); + + mContext->setStateApproved(); + QCOMPARE(spyEnterPacePassword.count(), 1); + + mContext->setPin(QString("111111")); + mContext->setStateApproved(false); + mContext->setStateApproved(); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_RunCan() + { + const auto& message = createMessage(PacePasswordId::PACE_CAN); + mContext->setEstablishPaceChannelMessage(message); + QSignalSpy spyEnterPacePassword(mState.data(), &StatePreparePaceRemote::fireEnterPacePassword); + QSignalSpy spyContinue(mState.data(), &StatePreparePaceRemote::fireContinue); + + mContext->setStateApproved(); + QCOMPARE(spyEnterPacePassword.count(), 1); + + mContext->setCan(QString("111111")); + mContext->setStateApproved(false); + mContext->setStateApproved(); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_RunPuk() + { + const auto& message = createMessage(PacePasswordId::PACE_PUK); + mContext->setEstablishPaceChannelMessage(message); + QSignalSpy spyEnterPacePassword(mState.data(), &StatePreparePaceRemote::fireEnterPacePassword); + QSignalSpy spyContinue(mState.data(), &StatePreparePaceRemote::fireContinue); + + mContext->setStateApproved(); + QCOMPARE(spyEnterPacePassword.count(), 1); + + mContext->setPuk(QString("111111")); + mContext->setStateApproved(false); + mContext->setStateApproved(); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_RunUnknown() + { + const auto& message = createMessage(PacePasswordId::UNKNOWN); + mContext->setEstablishPaceChannelMessage(message); + QSignalSpy spyContinue(mState.data(), &StatePreparePaceRemote::fireContinue); + + QTest::ignoreMessage(QtCriticalMsg, "Cannot handle PacePasswordId: UNKNOWN"); + mContext->setStateApproved(); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_RunPinPadModeFalse() + { + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(false); + QSignalSpy spyContinue(mState.data(), &StatePreparePaceRemote::fireContinue); + + mContext->setStateApproved(); + QCOMPARE(spyContinue.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StatePreparePaceRemote) +#include "test_StatePreparePaceRemote.moc" diff --git a/test/qt/core/states/test_StateProcessCertificatesFromEac2.cpp b/test/qt/core/states/test_StateProcessCertificatesFromEac2.cpp index 71a80ce..968fd16 100644 --- a/test/qt/core/states/test_StateProcessCertificatesFromEac2.cpp +++ b/test/qt/core/states/test_StateProcessCertificatesFromEac2.cpp @@ -7,10 +7,11 @@ #include "states/StateProcessCertificatesFromEac2.h" #include "asn1/CVCertificateChainBuilder.h" -#include "Commands.h" #include "paos/retrieve/DidAuthenticateEac1.h" #include "paos/retrieve/DidAuthenticateEac1Parser.h" #include "paos/retrieve/DidAuthenticateEac2Parser.h" +#include "states/StateBuilder.h" + #include "TestAuthContext.h" #include "TestFileHelper.h" @@ -25,7 +26,7 @@ class test_StateProcessCertificatesFromEac2 { Q_OBJECT QScopedPointer mState; - QSharedPointer mAuthContext; + QSharedPointer mAuthContext; Q_SIGNALS: void fireStateStart(QEvent* pEvent); @@ -40,7 +41,7 @@ class test_StateProcessCertificatesFromEac2 paceOutput.parse(QByteArray::fromHex(TestFileHelper::readFile(":/card/EstablishPaceChannelOutput.hex")), PacePasswordId::PACE_PIN); mAuthContext->setPaceOutputData(paceOutput); - mState.reset(new StateProcessCertificatesFromEac2(mAuthContext)); + mState.reset(StateBuilder::createState(mAuthContext)); mState->setStateName("StateProcessCertificatesFromEac2"); connect(this, &test_StateProcessCertificatesFromEac2::fireStateStart, mState.data(), &AbstractState::onEntry); } @@ -69,8 +70,8 @@ class test_StateProcessCertificatesFromEac2 void testDoNotTakeTerminalCvcFromEac2() { // move terminal cvc from eac1 to eac2 - mAuthContext->mDIDAuthenticateEAC2->mEac2.mCvCertificates.append(mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.at(0)); - mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.removeAt(0); + mAuthContext->mDIDAuthenticateEAC2->mEac2.mCvCertificates.append(mAuthContext->getDidAuthenticateEac1()->getCvCertificates().at(0)); + mAuthContext->clearCvCertificates(); mAuthContext->initCvcChainBuilder(); QSignalSpy spy(mState.data(), &StateProcessCertificatesFromEac2::fireAbort); @@ -85,10 +86,10 @@ class test_StateProcessCertificatesFromEac2 void testCvcFromEac2() { // move terminal dv from eac1 to eac2 - mAuthContext->mDIDAuthenticateEAC2->mEac2.mCvCertificates.append(mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.at(1)); - mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.removeAt(3); - mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.removeAt(2); - mAuthContext->mDIDAuthenticateEAC1->mEac1InputType.mCvCertificates.removeAt(1); + mAuthContext->mDIDAuthenticateEAC2->mEac2.mCvCertificates.append(mAuthContext->getDidAuthenticateEac1()->getCvCertificates().at(1)); + mAuthContext->removeCvCertAt(3); + mAuthContext->removeCvCertAt(2); + mAuthContext->removeCvCertAt(1); mAuthContext->initCvcChainBuilder(); QSignalSpy spy(mState.data(), &StateProcessCertificatesFromEac2::fireContinue); diff --git a/test/qt/core/states/test_StateProcessRemoteMessages.cpp b/test/qt/core/states/test_StateProcessRemoteMessages.cpp index 8d2c428..43befc1 100644 --- a/test/qt/core/states/test_StateProcessRemoteMessages.cpp +++ b/test/qt/core/states/test_StateProcessRemoteMessages.cpp @@ -9,7 +9,7 @@ #include "MockCardConnectionWorker.h" #include "MockRemoteServer.h" -#include +#include using namespace governikus; @@ -33,7 +33,7 @@ class test_StateProcessRemoteMessages const QSharedPointer context(new RemoteServiceContext()); StateProcessRemoteMessages state(context); state.run(); - QCOMPARE(state.mConnections.size(), 1); + QCOMPARE(state.mConnections.size(), 2); QCOMPARE(state.mMessageConnections.size(), 4); } diff --git a/test/qt/core/states/test_StateSelectPasswordId.cpp b/test/qt/core/states/test_StateSelectPasswordId.cpp index cff4412..c770199 100644 --- a/test/qt/core/states/test_StateSelectPasswordId.cpp +++ b/test/qt/core/states/test_StateSelectPasswordId.cpp @@ -6,6 +6,8 @@ #include "states/StateSelectPasswordId.h" +#include "states/StateBuilder.h" + #include using namespace governikus; @@ -20,18 +22,21 @@ class test_StateSelectPasswordId void test_Run() { QSharedPointer context(new WorkflowContext()); - StateSelectPasswordId id(context); - QSignalSpy spyPasswordIdCAN(&id, &StateSelectPasswordId::firePasswordIdCAN); - QSignalSpy spyContinue(&id, &StateSelectPasswordId::fireContinue); + const auto& state = StateBuilder::createState(context); + QSignalSpy spyPasswordIdCAN(state, &StateSelectPasswordId::firePasswordIdCAN); + QSignalSpy spyContinue(state, &StateSelectPasswordId::fireContinue); context->setCanAllowedMode(true); + context->setStateApproved(); QTest::ignoreMessage(QtDebugMsg, "CAN allowed: true"); - id.run(); + QTest::ignoreMessage(QtDebugMsg, "Running state \"StateSelectPasswordId\""); + state->onStateApprovedChanged(); QCOMPARE(spyPasswordIdCAN.count(), 1); context->setCanAllowedMode(false); QTest::ignoreMessage(QtDebugMsg, "CAN allowed: false"); - id.run(); + QTest::ignoreMessage(QtDebugMsg, "Running state \"StateSelectPasswordId\""); + state->onStateApprovedChanged(); QCOMPARE(spyContinue.count(), 1); } diff --git a/test/qt/core/states/test_StateSelectReader.cpp b/test/qt/core/states/test_StateSelectReader.cpp index 4590ad6..c52d13e 100644 --- a/test/qt/core/states/test_StateSelectReader.cpp +++ b/test/qt/core/states/test_StateSelectReader.cpp @@ -6,6 +6,10 @@ #include "states/StateSelectReader.h" +#include "Env.h" +#include "ReaderManager.h" +#include "states/StateBuilder.h" + #include using namespace governikus; @@ -15,65 +19,63 @@ class test_StateSelectReader : public QObject { Q_OBJECT + QScopedPointer mState; + QSharedPointer mContext; private Q_SLOTS: - void test_RequiresCard() + void init() { - const QSharedPointer context(new WorkflowContext()); - StateSelectReader state(context); + mContext.reset(new WorkflowContext()); + mState.reset(StateBuilder::createState(mContext)); + } - QVERIFY(state.requiresCard(ReaderManagerPlugInType::PCSC)); - QVERIFY(state.requiresCard(ReaderManagerPlugInType::REMOTE)); - QVERIFY(!state.requiresCard(ReaderManagerPlugInType::UNKNOWN)); - QVERIFY(!state.requiresCard(ReaderManagerPlugInType::BLUETOOTH)); + + void cleanup() + { + mState.reset(); + mContext.clear(); } void test_OnReaderInfoChangedNoSelectableReaders() { - const QSharedPointer context(new WorkflowContext()); - context->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC, ReaderManagerPlugInType::UNKNOWN, ReaderManagerPlugInType::REMOTE}); - StateSelectReader state(context); + mContext->setReaderPlugInTypes({ReaderManagerPlugInType::PCSC, ReaderManagerPlugInType::UNKNOWN, ReaderManagerPlugInType::REMOTE}); + mContext->setStateApproved(); + mState->onStateApprovedChanged(); + const auto readerManager = Env::getSingleton(); QTest::ignoreMessage(QtDebugMsg, "No selectable reader detected"); - state.onReaderInfoChanged(); + Q_EMIT readerManager->fireReaderAdded(QString()); } void test_OnReaderDeviceError() { - const QSharedPointer context(new WorkflowContext()); - StateSelectReader state(context); - QSignalSpy spyAbort(&state, &StateSelectReader::fireAbort); + QSignalSpy spyAbort(mState.data(), &StateSelectReader::fireAbort); + mContext->setStateApproved(); + mState->onStateApprovedChanged(); + const auto readerManager = Env::getSingleton(); - state.onReaderDeviceError(GlobalStatus::Code::No_Error); + Q_EMIT readerManager->fireReaderDeviceError(GlobalStatus::Code::No_Error); QCOMPARE(spyAbort.count(), 0); - state.onReaderDeviceError(GlobalStatus::Code::Workflow_Reader_Device_Scan_Error); + Q_EMIT readerManager->fireReaderDeviceError(GlobalStatus::Code::Workflow_Reader_Device_Scan_Error); QCOMPARE(spyAbort.count(), 0); - state.onReaderDeviceError(GlobalStatus::Code::Card_Communication_Error); + Q_EMIT readerManager->fireReaderDeviceError(GlobalStatus::Code::Card_Communication_Error); QCOMPARE(spyAbort.count(), 1); - QCOMPARE(context->getStatus().getStatusCode(), GlobalStatus::Code::Card_Communication_Error); + QCOMPARE(mContext->getStatus().getStatusCode(), GlobalStatus::Code::Card_Communication_Error); } void test_OnEntry() { - const QSharedPointer context(new WorkflowContext()); - StateSelectReader state(context); - const QString stateName("name"); - state.setStateName(stateName); + QSignalSpy spyRetry(mState.data(), &StateSelectReader::fireRetry); - QSignalSpy spyRetry(&state, &StateSelectReader::fireRetry); + mState->onEntry(nullptr); - state.onEntry(nullptr); - - Q_EMIT context->fireAbortCardSelection(); + Q_EMIT mContext->fireReaderPlugInTypesChanged(); QCOMPARE(spyRetry.count(), 1); - - Q_EMIT context->fireReaderPlugInTypesChanged(); - QCOMPARE(spyRetry.count(), 2); } diff --git a/test/qt/core/states/test_StateSendWhitelistSurvey.cpp b/test/qt/core/states/test_StateSendWhitelistSurvey.cpp new file mode 100644 index 0000000..7ecd6fd --- /dev/null +++ b/test/qt/core/states/test_StateSendWhitelistSurvey.cpp @@ -0,0 +1,180 @@ +/*! + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateSendWhitelistSurvey.h" + +#include "AppSettings.h" +#include "HttpServer.h" +#include "LogHandler.h" +#include "states/StateBuilder.h" +#include "SecureStorage.h" + +#include "MockCardConnectionWorker.h" +#include "MockRemoteServer.h" +#include "TestAuthContext.h" + +#include + + +using namespace governikus; + +class MockSecureStorage + : public SecureStorage +{ + Q_GADGET + + public: + explicit MockSecureStorage(const QUrl& url) + : SecureStorage() + { + mWhitelistServerBaseUrl = url; + } + + + virtual ~MockSecureStorage() override; +}; + +MockSecureStorage::~MockSecureStorage() +{ +} + + +class test_StateSendWhitelistSurvey + : public QObject +{ + Q_OBJECT + QSharedPointer mState; + QSharedPointer mContext; + + private Q_SLOTS: + void init() + { + mContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1.xml")); + mState.reset(StateBuilder::createState(mContext)); + mState->onEntry(nullptr); + } + + + void cleanup() + { + mState.clear(); + mContext.clear(); + Env::getSingleton()->resetBacklog(); + } + + + void test_Run_NoSurveyPending() + { + Env::getSingleton()->getGeneralSettings().setDeviceSurveyPending(false); + QSignalSpy spy(mState.data(), &StateSendWhitelistSurvey::fireContinue); + + QTest::ignoreMessage(QtDebugMsg, "No survey pending."); + mContext->setStateApproved(); + QCOMPARE(spy.count(), 1); + } + + + void test_Run_StatusIsError() + { + // Implementation at this time not possible. + // Q_ASSERT handling unsupported. + } + + + void test_Run_EffectiveAccessRightsEmpty() + { + // Implementation at this time not possible. + // Q_ASSERT handling unsupported. + } + + + void test_Run_DidAuthenticateEac1IsNullptr() + { + // Implementation at this time not possible. + // Q_ASSERT handling unsupported. + } + + + void test_Run_data() + { + QTest::addColumn("port"); + QTest::addColumn("spyCounter"); + QTest::addColumn("url"); + + QTest::newRow("successful") << quint16(25000) << 1 << QUrl("http://localhost:25000"); + QTest::newRow("invalidPort") << quint16(1515) << 0 << QUrl("http://localhost:25000"); + QTest::newRow("emptyPort") << quint16() << 0 << QUrl("http://localhost:25000"); + QTest::newRow("emptyPortinUrl") << quint16(1515) << 0 << QUrl("http://localhost"); + QTest::newRow("invalidHost") << quint16(25000) << 0 << QUrl("http://test:25000"); + } + + + void test_Run() + { + QFETCH(quint16, port); + QFETCH(int, spyCounter); + QFETCH(QUrl, url); + + Env::getSingleton()->init(); + Env::getSingleton()->getGeneralSettings().setDeviceSurveyPending(true); + QSignalSpy spyContinue(mState.data(), &StateSendWhitelistSurvey::fireContinue); + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); + + HttpServer server(port); + QSignalSpy spy(&server, &HttpServer::fireNewHttpRequest); + + const QString name("reader"); + mContext->setReaderName(name); + MockSecureStorage storage(url); + Env::set(SecureStorage::staticMetaObject, &storage); + + mContext->setStateApproved(); + QTRY_COMPARE(spy.count(), spyCounter); + QVERIFY(!Env::getSingleton()->getGeneralSettings().isDeviceSurveyPending()); + QCOMPARE(spyContinue.count(), 1); + + bool surveySent = false; + QString message; + for (int i = 0; i < spyLog.size(); i++) + { + if (spyLog.at(i).at(0).toString().contains("Sent survey to whitelist server:")) + { + surveySent = true; + message = spyLog.at(i).at(0).toString(); + break; + } + } + + QVERIFY(surveySent); + QVERIFY(message.contains("AusweisAppVersionNumber")); + QVERIFY(message.contains("ModelName")); + QVERIFY(message.contains("ModelNumber")); + QVERIFY(message.contains("Rom")); + QVERIFY(message.contains("AndroidVersion")); + QVERIFY(message.contains("BuildNumber")); + QVERIFY(message.contains("KernelVersion")); + QVERIFY(message.contains("\"MaximumNfcPacketLength\": 0")); + QVERIFY(message.contains("Vendor")); + + if (spyCounter != 0) + { + const QSharedPointer request = spy.at(0).at(0).value >(); + QCOMPARE(request->getUrl(), QUrl("/new")); + QCOMPARE(request->getHeader(QByteArray("host")), QByteArray("localhost:25000")); + QCOMPARE(request->getHeader(QByteArray("content-type")), QByteArray("application/json; charset=UTF-8")); + QJsonDocument json = QJsonDocument::fromJson(request->getBody()); + QJsonObject jsonObject = json.object(); + QVERIFY(jsonObject.contains("AusweisAppVersionNumber")); + QVERIFY(jsonObject.contains("ModelName")); + QVERIFY(jsonObject.contains("ModelNumber")); + QVERIFY(jsonObject.contains("Rom")); + QVERIFY(jsonObject.contains("Vendor")); + } + } + + +}; + +QTEST_GUILESS_MAIN(test_StateSendWhitelistSurvey) +#include "test_StateSendWhitelistSurvey.moc" diff --git a/test/qt/core/states/test_StateStartPaosResponse.cpp b/test/qt/core/states/test_StateStartPaosResponse.cpp index 5d13618..e645ead 100644 --- a/test/qt/core/states/test_StateStartPaosResponse.cpp +++ b/test/qt/core/states/test_StateStartPaosResponse.cpp @@ -2,8 +2,8 @@ * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include #include "states/StateBuilder.h" diff --git a/test/qt/core/states/test_StateStartRemoteService.cpp b/test/qt/core/states/test_StateStartRemoteService.cpp index 18a5b49..dc2dee3 100644 --- a/test/qt/core/states/test_StateStartRemoteService.cpp +++ b/test/qt/core/states/test_StateStartRemoteService.cpp @@ -6,8 +6,9 @@ #include "MockCardConnectionWorker.h" #include "MockRemoteServer.h" +#include "states/StateBuilder.h" -#include +#include using namespace governikus; @@ -29,12 +30,25 @@ class test_StateStartRemoteService void test_Run() { const QSharedPointer context(new RemoteServiceContext()); - StateStartRemoteService state(context); - QSignalSpy spyContinue(&state, &StateStartRemoteService::fireContinue); + const QSharedPointer state(StateBuilder::createState(context)); + QSignalSpy spyContinue(state.data(), &StateStartRemoteService::fireContinue); - state.run(); + state->onEntry(nullptr); + context->setStateApproved(); QCOMPARE(spyContinue.count(), 1); - state.onMessageHandlerAdded(nullptr); + + const auto& server = context->getRemoteServer(); + const QSharedPointer handler(new ServerMessageHandlerImpl(nullptr)); + Q_EMIT server->fireMessageHandlerAdded(handler); + Q_EMIT handler->fireClosed(); + QCOMPARE(context->getNewPin(), QString()); + QCOMPARE(context->getPin(), QString()); + QCOMPARE(context->getCan(), QString()); + QCOMPARE(context->getPuk(), QString()); + QCOMPARE(context->getCardConnection(), QSharedPointer()); + QCOMPARE(context->getLastPaceResult(), CardReturnCode::OK); + QCOMPARE(context->getEstablishPaceChannelMessage(), QSharedPointer()); + QCOMPARE(context->getModifyPinMessage(), QSharedPointer()); } diff --git a/test/qt/core/states/test_StateStopRemoteService.cpp b/test/qt/core/states/test_StateStopRemoteService.cpp index f831707..570440e 100644 --- a/test/qt/core/states/test_StateStopRemoteService.cpp +++ b/test/qt/core/states/test_StateStopRemoteService.cpp @@ -5,11 +5,12 @@ #include "states/remote_service/StateStopRemoteService.h" #include "context/RemoteServiceContext.h" +#include "states/StateBuilder.h" #include "MockCardConnectionWorker.h" #include "MockRemoteServer.h" -#include +#include using namespace governikus; @@ -18,6 +19,8 @@ class test_StateStopRemoteService : public QObject { Q_OBJECT + QSharedPointer mState; + QSharedPointer mContext; private Q_SLOTS: void initTestCase() @@ -28,25 +31,35 @@ class test_StateStopRemoteService } + void init() + { + mContext.reset(new RemoteServiceContext()); + mState.reset(StateBuilder::createState(mContext)); + } + + + void cleanup() + { + mContext.clear(); + mState.clear(); + } + + void test_Run() { - const QSharedPointer context(new RemoteServiceContext()); - StateStopRemoteService state(context); - QSignalSpy spyContinue(&state, &StateStopRemoteService::fireContinue); - - state.run(); + QSignalSpy spyContinue(mState.data(), &StateStopRemoteService::fireContinue); + mState->onEntry(nullptr); + mContext->setStateApproved(); QCOMPARE(spyContinue.count(), 1); } void test_OnExit() { - const QSharedPointer context(new RemoteServiceContext()); - StateStopRemoteService state(context); const QString name("name"); - state.setStateName(name); - state.onExit(nullptr); - QVERIFY(!context->getRemoteServer()->isRunning()); + mState->setStateName(name); + Q_EMIT mContext->fireCancelWorkflow(); + QVERIFY(!mContext->getRemoteServer()->isRunning()); } diff --git a/test/qt/core/states/test_StateVerifyRetryCounter.cpp b/test/qt/core/states/test_StateVerifyRetryCounter.cpp new file mode 100644 index 0000000..99f2fc1 --- /dev/null +++ b/test/qt/core/states/test_StateVerifyRetryCounter.cpp @@ -0,0 +1,133 @@ +/*! + * \brief Unit tests for \ref StateVerifyRetryCounter + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateVerifyRetryCounter.h" + +#include "states/StateBuilder.h" + +#include "MockCardConnectionWorker.h" + +#include +#include + + +using namespace governikus; + +class test_StateVerifyRetryCounter + : public QObject +{ + Q_OBJECT + QSharedPointer mState; + QSharedPointer mContext; + QThread mWorkerThread; + + private Q_SLOTS: + void init() + { + mContext.reset(new WorkflowContext()); + mState.reset(StateBuilder::createState(mContext)); + mState->onEntry(nullptr); + mWorkerThread.start(); + } + + + void cleanup() + { + mWorkerThread.quit(); + mWorkerThread.wait(); + mState.clear(); + mContext.clear(); + } + + + void test_Run() + { + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&mWorkerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 3); + const ReaderInfo readerInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo); + Q_EMIT worker->fireReaderInfoChanged(readerInfo); + mContext->setCardConnection(connection); + mContext->setExpectedRetryCounter(3); + QSignalSpy spyContinue(mState.data(), &StateVerifyRetryCounter::fireContinue); + + QTest::ignoreMessage(QtDebugMsg, "Retry counter | actual: 3 / expected: 3"); + mContext->setStateApproved(); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_Run_NoConnection() + { + QSignalSpy spyAbort(mState.data(), &StateVerifyRetryCounter::fireAbort); + + QTest::ignoreMessage(QtDebugMsg, "Card connection lost."); + mContext->setStateApproved(); + QCOMPARE(spyAbort.count(), 1); + } + + + void test_Run_NotExpectedReader() + { + const QString password("000000"); + mContext->setPin(password); + mContext->setCan(password); + mContext->setPuk(password); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&mWorkerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 2); + const ReaderInfo readerInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo); + Q_EMIT worker->fireReaderInfoChanged(readerInfo); + mContext->setCardConnection(connection); + mContext->setExpectedRetryCounter(2); + mContext->setReaderName(QString("test reader")); + QSignalSpy spyContinue(mState.data(), &StateVerifyRetryCounter::fireContinue); + + QTest::ignoreMessage(QtDebugMsg, "Retry counter | actual: 2 / expected: 2"); + QTest::ignoreMessage(QtDebugMsg, "The reader changed or the connected card has an unexpected retry counter. Clearing PACE passwords."); + mContext->setStateApproved(); + QCOMPARE(mContext->getPin(), QString()); + QCOMPARE(mContext->getCan(), QString()); + QCOMPARE(mContext->getPuk(), QString()); + QVERIFY(mContext->isExpectedReader()); + QCOMPARE(mContext->getExpectedRetryCounter(), 2); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_Run_ActualNotEqualExpectedRetryCounter() + { + const QString password("000000"); + mContext->setPin(password); + mContext->setCan(password); + mContext->setPuk(password); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&mWorkerThread); + const QSharedPointer connection(new CardConnection(worker)); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), 2); + const ReaderInfo readerInfo(QString(), ReaderManagerPlugInType::UNKNOWN, cardInfo); + Q_EMIT worker->fireReaderInfoChanged(readerInfo); + mContext->setCardConnection(connection); + QSignalSpy spyContinue(mState.data(), &StateVerifyRetryCounter::fireContinue); + + QTest::ignoreMessage(QtDebugMsg, "Retry counter | actual: 2 / expected: -1"); + QTest::ignoreMessage(QtDebugMsg, "The reader changed or the connected card has an unexpected retry counter. Clearing PACE passwords."); + mContext->setStateApproved(); + QCOMPARE(mContext->getPin(), QString()); + QCOMPARE(mContext->getCan(), QString()); + QCOMPARE(mContext->getPuk(), QString()); + QVERIFY(mContext->isExpectedReader()); + QCOMPARE(mContext->getExpectedRetryCounter(), 2); + QCOMPARE(spyContinue.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateVerifyRetryCounter) +#include "test_StateVerifyRetryCounter.moc" diff --git a/test/qt/core/states/test_StateWriteHistory.cpp b/test/qt/core/states/test_StateWriteHistory.cpp new file mode 100644 index 0000000..e1b2de9 --- /dev/null +++ b/test/qt/core/states/test_StateWriteHistory.cpp @@ -0,0 +1,93 @@ +/*! + * \brief Unit tests for \ref StateWriteHistory + * + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "states/StateWriteHistory.h" + +#include "AppSettings.h" +#include "Env.h" +#include "states/StateBuilder.h" + +#include "MockCardConnectionWorker.h" +#include "TestAuthContext.h" + +#include +#include + + +using namespace governikus; + +class test_StateWriteHistory + : public QObject +{ + Q_OBJECT + QSharedPointer mContext; + QSharedPointer mState; + + private Q_SLOTS: + void init() + { + mContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1_2.xml")); + mState.reset(StateBuilder::createState(mContext)); + mState->onEntry(nullptr); + } + + + void cleanup() + { + mContext.clear(); + mState.clear(); + } + + + void test_RunHistoryDisabled() + { + QSignalSpy spyContinue(mState.data(), &StateWriteHistory::fireContinue); + Env::getSingleton()->getHistorySettings().setEnabled(false); + + QTest::ignoreMessage(QtDebugMsg, "History disabled"); + mContext->setStateApproved(); + QCOMPARE(spyContinue.count(), 1); + } + + + void test_RunNoEffectiveAccessRights() + { + QSignalSpy spyAbort(mState.data(), &StateWriteHistory::fireAbort); + Env::getSingleton()->getHistorySettings().setEnabled(true); + mContext->setEffectiveAccessRights(QSet()); + + QTest::ignoreMessage(QtWarningMsg, "No EAC1 structure or effective CHAT in model."); + mContext->setStateApproved(); + QCOMPARE(spyAbort.count(), 1); + } + + + void test_RunNoError() + { + auto& historySettings = Env::getSingleton()->getHistorySettings(); + QSignalSpy spyContinue(mState.data(), &StateWriteHistory::fireContinue); + historySettings.setEnabled(true); + const QSet rights = {AccessRight::READ_DG01, AccessRight::READ_DG02}; + mContext->setEffectiveAccessRights(rights); + + mContext->setStatus(GlobalStatus::Code::No_Error); + mContext->setStateApproved(); + const auto historyInfo = historySettings.getHistoryInfos().at(0); + QCOMPARE(historyInfo.getSubjectName(), mState->getContext()->getDidAuthenticateEac1()->getCertificateDescription()->getSubjectName()); + QCOMPARE(historyInfo.getSubjectUrl(), mState->getContext()->getDidAuthenticateEac1()->getCertificateDescription()->getSubjectUrl()); + QCOMPARE(historyInfo.getPurpose(), mState->getContext()->getDidAuthenticateEac1()->getCertificateDescription()->getPurpose()); + QVERIFY(historyInfo.getTermOfUsage().contains(mState->getContext()->getDidAuthenticateEac1()->getCertificateDescription()->getTermsOfUsage())); + qDebug() << historyInfo.getRequestedData(); + QStringList list = {"DocumentType", "IssuingCountry"}; + QCOMPARE(historyInfo.getRequestedData(), list); + QCOMPARE(spyContinue.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_StateWriteHistory) +#include "test_StateWriteHistory.moc" diff --git a/test/qt/core/test_CertificateChecker.cpp b/test/qt/core/test_CertificateChecker.cpp index b6bb4ef..c6107e7 100644 --- a/test/qt/core/test_CertificateChecker.cpp +++ b/test/qt/core/test_CertificateChecker.cpp @@ -25,7 +25,7 @@ class test_CertificateChecker private Q_SLOTS: void initTestCase() { - certs = SecureStorage::getInstance().getUpdateCertificates(); + certs = Env::getSingleton()->getUpdateCertificates(); QVERIFY(certs.size() > 0); } diff --git a/test/qt/core/test_DiagnosisAntivirusDetection.cpp b/test/qt/core/test_DiagnosisAntivirusDetection.cpp index 734154e..2e63a54 100644 --- a/test/qt/core/test_DiagnosisAntivirusDetection.cpp +++ b/test/qt/core/test_DiagnosisAntivirusDetection.cpp @@ -1,7 +1,7 @@ /*! * \brief Unit tests for \ref DiagnosisAntivirusDetection * - * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany */ #include "DiagnosisAntivirusDetection.h" diff --git a/test/qt/core/test_DiagnosisConnectionTest.cpp b/test/qt/core/test_DiagnosisConnectionTest.cpp index 4f9ca44..f936f53 100644 --- a/test/qt/core/test_DiagnosisConnectionTest.cpp +++ b/test/qt/core/test_DiagnosisConnectionTest.cpp @@ -118,6 +118,37 @@ class test_DiagnosisConnectionTest } + void test_StartConnectionTest_NoProxy() + { + QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy); + mTest->startConnectionTest(); + QVERIFY(mTest->mProxyPingDone); + QVERIFY(!mTest->mConnectionTestWithoutProxyDone); + QVERIFY(mTest->mConnectionTestWithProxyDone); + QVERIFY(!mTest->mIsProxySet); + QCOMPARE(mTest->mTcpSocketWithoutProxy.proxy(), QNetworkProxy::NoProxy); + QCOMPARE(mTest->mTcpSocketWithoutProxy.state(), QAbstractSocket::SocketState::HostLookupState); + } + + + void test_StartConnectionTest() + { + QNetworkProxy testProxy(QNetworkProxy::ProxyType::HttpProxy, QString("localhost"), 25000); + QNetworkProxy::setApplicationProxy(testProxy); + + mTest->startConnectionTest(); + QVERIFY(mTest->mIsProxySet); + QCOMPARE(mTest->mProxyHostName, QString("localhost")); + QCOMPARE(mTest->mProxyPort, QString("25000")); + QCOMPARE(mTest->mProxyType, QString("HttpProxy")); + QCOMPARE(mTest->mProxyType, QString("HttpProxy")); + QCOMPARE(mTest->mPingSocketToProxy.proxy(), QNetworkProxy::NoProxy); + QCOMPARE(mTest->mPingSocketToProxy.state(), QAbstractSocket::SocketState::HostLookupState); + QCOMPARE(mTest->mTcpSocketWithProxy.proxy(), testProxy); + QCOMPARE(mTest->mTcpSocketWithProxy.state(), QAbstractSocket::SocketState::ConnectingState); + } + + }; QTEST_GUILESS_MAIN(test_DiagnosisConnectionTest) diff --git a/test/qt/core/test_DiagnosisModel.cpp b/test/qt/core/test_DiagnosisModel.cpp index b66a9a5..5ac0231 100644 --- a/test/qt/core/test_DiagnosisModel.cpp +++ b/test/qt/core/test_DiagnosisModel.cpp @@ -19,6 +19,46 @@ class test_DiagnosisModel QSharedPointer mContext; QSharedPointer mModel; + private: + QString getTestData(const QString& pFilename) + { + QString filePath = QStringLiteral(":/core/diagnosis/") + pFilename; + QByteArray rawData = TestFileHelper::readFile(filePath); + return QString::fromUtf8(rawData); + } + + + bool verifyOrder(const QSharedPointer& pSection, + const QVector >& pSectionOrder) + { + QVector > > contentOrder; + for (const auto& section : pSectionOrder) + { + contentOrder.append(section->mContentItems); + } + + return verifyOrder(pSection->mContentItems, contentOrder); + } + + + bool verifyOrder(const QVector >& pSection, + const QVector > >& pOrder) + { + int offset = 0; + for (const auto& subsection : pOrder) + { + const auto& slice = pSection.mid(offset, subsection.size()); + if (slice != subsection) + { + return false; + } + offset += subsection.size(); + } + + return offset == pSection.size(); + } + + private Q_SLOTS: void init() { @@ -37,44 +77,42 @@ class test_DiagnosisModel void test_newDiagnosisModel() { QCOMPARE(mModel->mContext, mContext); - QCOMPARE(mModel->mRootItem->getText(), QString("Diagnosis data")); - QCOMPARE(mModel->mOperatingSystemItem->getText(), QString("Operating system")); - QCOMPARE(mModel->mReaderItem->getText(), QString("Card reader")); - QCOMPARE(mModel->mPcScItem->getText(), QString("PC/SC")); - QCOMPARE(mModel->mPairedDevices->getText(), QString("Paired devices")); - QCOMPARE(mModel->mNetworkInterfaces->getText(), QString("Network interfaces")); - QCOMPARE(mModel->mNetworkConnectionTest->getText(), QString("Network connection test")); - QCOMPARE(mModel->mInstalledAntivirus->getText(), QString("Installed antivirus software")); - QCOMPARE(mModel->mWindowsFirewall->getText(), QString("Firewall")); - QCOMPARE(mModel->mTimestampItem->getText(), QString("Time of diagnosis")); + QCOMPARE(mModel->mSections.size(), 4); - QCOMPARE(mModel->mRootItem->childCount(), 10); - QCOMPARE(mModel->mRootItem->getChild(0), mModel->mAppVersionItem); - QCOMPARE(mModel->mRootItem->getChild(1), mModel->mOperatingSystemItem); - QCOMPARE(mModel->mRootItem->getChild(2), mModel->mReaderItem); - QCOMPARE(mModel->mRootItem->getChild(3), mModel->mPcScItem); - QCOMPARE(mModel->mRootItem->getChild(4), mModel->mPairedDevices); - QCOMPARE(mModel->mRootItem->getChild(5), mModel->mNetworkInterfaces); - QCOMPARE(mModel->mRootItem->getChild(6), mModel->mNetworkConnectionTest); - QCOMPARE(mModel->mRootItem->getChild(7), mModel->mInstalledAntivirus); - QCOMPARE(mModel->mRootItem->getChild(8), mModel->mWindowsFirewall); - QCOMPARE(mModel->mRootItem->getChild(9), mModel->mTimestampItem); + QCOMPARE(mModel->mSections.at(0).first, QCoreApplication::applicationName()); + QCOMPARE(mModel->mSections.at(1).first, QString("Card reader")); + QCOMPARE(mModel->mSections.at(2).first, QString("Network")); + QCOMPARE(mModel->mSections.at(3).first, QString("Antivirus and firewall")); - QCOMPARE(mModel->mOperatingSystemItem->childCount(), 3); + QCOMPARE(mModel->mSections.at(0).second->mContentItems.last(), mModel->mTimestampItem); - QCOMPARE(mModel->mReaderItem->childCount(), 1); - QCOMPARE(mModel->mReaderItem->getChild(0)->getText(), QString("Diagnosis is running...")); + QCOMPARE(mModel->mSections.at(1).second, mModel->mCombinedReaderSection); + QCOMPARE(mModel->mSections.at(2).second, mModel->mCombinedNetworkSection); + QCOMPARE(mModel->mSections.at(3).second, mModel->mCombinedAntivirusFirewallSection); - QCOMPARE(mModel->mNetworkConnectionTest->childCount(), 1); - QCOMPARE(mModel->mNetworkConnectionTest->getChild(0)->getText(), QString("Diagnosis is running...")); + QCOMPARE(mModel->mSections.at(1).second->mContentItems.at(0)->mTitle, QString("Card reader")); + QCOMPARE(mModel->mSections.at(1).second->mContentItems.at(1)->mContent, QString("Diagnosis is running...")); + QCOMPARE(mModel->mSections.at(1).second->mContentItems.at(2)->mTitle, QString("Paired remote devices")); + QCOMPARE(mModel->mSections.at(1).second->mContentItems.at(4)->mTitle, QString("PC/SC information")); + QCOMPARE(mModel->mSections.at(1).second->mContentItems.at(5)->mContent, QString("Diagnosis is running...")); + QCOMPARE(mModel->mSections.at(1).second->mContentItems.mid(0, 2), mModel->mCardReaderSection->mContentItems); + QCOMPARE(mModel->mSections.at(1).second->mContentItems.mid(2, 2), mModel->mRemoteDeviceSection->mContentItems); + QCOMPARE(mModel->mSections.at(1).second->mContentItems.mid(4, 2), mModel->mPcscSection->mContentItems); + + QCOMPARE(true, verifyOrder(mModel->mSections.at(1).second, + {mModel->mCardReaderSection, mModel->mRemoteDeviceSection, mModel->mPcscSection})); + QCOMPARE(true, verifyOrder(mModel->mSections.at(2).second, + {mModel->mNetworkConnectionSection, mModel->mNetworkInterfaceSection})); + QCOMPARE(true, verifyOrder(mModel->mSections.at(3).second, + {mModel->mAntivirusSection, mModel->mFirewallSection})); } void test_OnReaderInfosChanged() { mModel->onReaderInfosChanged(); - QCOMPARE(mModel->mReaderItem->childCount(), 1); - QCOMPARE(mModel->mReaderItem->getChild(0)->getText(), QString("Not recognised")); + QCOMPARE(mModel->mCardReaderSection->mContentItems.size(), 2); + QCOMPARE(mModel->mCardReaderSection->mContentItems.at(1)->mContent, QString("No supported reader found.")); ReaderInfo defaultInfo; ReaderInfo infoEidCard(QString("testInfo"), ReaderManagerPlugInType::PCSC, CardInfo(CardType::EID_CARD)); @@ -84,45 +122,62 @@ class test_DiagnosisModel mContext->setReaderInfos(readerInfos); mModel->onReaderInfosChanged(); - QCOMPARE(mModel->mReaderItem->childCount(), 3); - QCOMPARE(mModel->mReaderItem->getChild(0)->getText(), QString()); - QCOMPARE(mModel->mReaderItem->getChild(1)->getText(), QString("testInfo")); - QCOMPARE(mModel->mReaderItem->getChild(2)->getText(), QString("name")); + QCOMPARE(mModel->mCardReaderSection->mContentItems.size(), 4); - auto child1 = mModel->mReaderItem->getChild(0); - QCOMPARE(child1->childCount(), 2); - QCOMPARE(child1->getChild(0)->getText(), QString("Type: Basic card reader")); - QCOMPARE(child1->getChild(1)->getText(), QString("Card: not inserted")); + QCOMPARE(mModel->mCardReaderSection->mContentItems.at(0)->mTitle, QString("Connected Card reader")); + QCOMPARE(mModel->mCardReaderSection->mContentItems.at(1)->mTitle, QString()); + QCOMPARE(mModel->mCardReaderSection->mContentItems.at(2)->mTitle, QString("testInfo")); + QCOMPARE(mModel->mCardReaderSection->mContentItems.at(3)->mTitle, QString("name")); - auto child2 = mModel->mReaderItem->getChild(1); - QCOMPARE(child2->childCount(), 3); - QCOMPARE(child2->getChild(0)->getText(), QString("Type: Basic card reader")); - QCOMPARE(child2->getChild(1)->getText(), QString("Card: ID card (PA/eAT)")); - QCOMPARE(child2->getChild(2)->getText(), QString("Retry counter: 4")); + QCOMPARE(mModel->mCardReaderSection->mContentItems.at(0)->mContent, QString()); + QCOMPARE(mModel->mCardReaderSection->mContentItems.at(1)->mContent, QString("Type: Basic card reader\nCard: not inserted")); + QCOMPARE(mModel->mCardReaderSection->mContentItems.at(2)->mContent, QString("Type: Basic card reader\nCard: ID card (PA/eAT)\nRetry counter: -1")); + QCOMPARE(mModel->mCardReaderSection->mContentItems.at(3)->mContent, QString("Type: Standard / comfort card reader\nCard: unknown type")); - auto child3 = mModel->mReaderItem->getChild(2); - QCOMPARE(child3->childCount(), 2); - QCOMPARE(child3->getChild(0)->getText(), QString("Type: Standard / comfort card reader")); - QCOMPARE(child3->getChild(1)->getText(), QString("Card: unknown type")); + QCOMPARE(true, verifyOrder(mModel->mCombinedReaderSection->mContentItems, { + mModel->mCardReaderSection->mContentItems, + mModel->mRemoteDeviceSection->mContentItems, + mModel->mPcscSection->mContentItems})); } - void test_onPcscInfoChanged() + void test_OnRemoteInfosChanged() + { + mModel->onRemoteInfosChanged(); + QCOMPARE(mModel->mRemoteDeviceSection->mContentItems.size(), 2); + QCOMPARE(mModel->mRemoteDeviceSection->mContentItems.at(0)->mTitle, QString("Paired remote devices")); + QCOMPARE(mModel->mRemoteDeviceSection->mContentItems.at(1)->mContent, QString("No devices paired.")); + QCOMPARE(true, verifyOrder(mModel->mCombinedReaderSection->mContentItems, { + mModel->mCardReaderSection->mContentItems, + mModel->mRemoteDeviceSection->mContentItems, + mModel->mPcscSection->mContentItems})); + } + + + void test_OnPcscInfoChanged() { const QString version("version"); - const DiagnosisContext::ComponentInfo component1; - const DiagnosisContext::ComponentInfo component2; - const DiagnosisContext::ComponentInfo driver1; - const DiagnosisContext::ComponentInfo driver2; + const DiagnosisContext::ComponentInfo component1(QString("/path/to/component1"), QString("description1"), QString("version1"), QString("vendor1")); + const DiagnosisContext::ComponentInfo component2(QString("/path/to/component2"), QString("description2"), QString("version2"), QString("vendor2")); + const DiagnosisContext::ComponentInfo driver1(QString("/path/to/driver1"), QString("description1"), QString("version1"), QString("vendor1")); + const DiagnosisContext::ComponentInfo driver2(QString("/path/to/driver2"), QString("description2"), QString("version2"), QString("vendor2")); QVector components = {component1, component2}; QVector drivers = {driver1, driver2}; mContext->setPcscInfo(version, components, drivers); - mModel->onPcscInfoChanged(); - QCOMPARE(mModel->mPcScItem->childCount(), 3); - QCOMPARE(mModel->mPcScItem->getChild(0)->getText(), QString("Version: version")); - QCOMPARE(mModel->mPcScItem->getChild(1)->getText(), QString("Components")); - QCOMPARE(mModel->mPcScItem->getChild(2)->getText(), QString("Driver")); + + QCOMPARE(mModel->mPcscSection->mContentItems.size(), 4); + + QCOMPARE(mModel->mPcscSection->mContentItems.at(0)->mTitle, QString("PC/SC information")); + QCOMPARE(mModel->mPcscSection->mContentItems.at(1)->mContent, QString("Version: version")); + QCOMPARE(mModel->mPcscSection->mContentItems.at(2)->mTitle, QString("Components")); + QCOMPARE(mModel->mPcscSection->mContentItems.at(2)->mContent, QString("description1\nVendor: vendor1\nVersion: version1\nFile path: /path/to/component1\ndescription2\nVendor: vendor2\nVersion: version2\nFile path: /path/to/component2")); + QCOMPARE(mModel->mPcscSection->mContentItems.at(3)->mTitle, QString("Driver")); + QCOMPARE(mModel->mPcscSection->mContentItems.at(3)->mContent, QString("description1\nVendor: vendor1\nVersion: version1\nFile path: /path/to/driver1\ndescription2\nVendor: vendor2\nVersion: version2\nFile path: /path/to/driver2")); + QCOMPARE(true, verifyOrder(mModel->mCombinedReaderSection->mContentItems, { + mModel->mCardReaderSection->mContentItems, + mModel->mRemoteDeviceSection->mContentItems, + mModel->mPcscSection->mContentItems})); } @@ -135,13 +190,13 @@ class test_DiagnosisModel mContext->setTimestamp(invalid); mModel->onTimestampChanged(); - QCOMPARE(mModel->mTimestampItem->childCount(), 1); - QCOMPARE(mModel->mTimestampItem->getChild(0)->getText(), QString("Initial diagnosis running, please wait.")); + QCOMPARE(mModel->mTimestampItem->mTitle, QString("Time of diagnosis")); + QCOMPARE(mModel->mTimestampItem->mContent, QString("Failed to retrieve date & time")); mContext->setTimestamp(valid); mModel->onTimestampChanged(); - QCOMPARE(mModel->mTimestampItem->childCount(), 1); - QCOMPARE(mModel->mTimestampItem->getChild(0)->getText(), QString("12. October 2018, 12:00:00 PM")); + QCOMPARE(mModel->mTimestampItem->mTitle, QString("Time of diagnosis")); + QCOMPARE(mModel->mTimestampItem->mContent, QString("12. October 2018, 12:00:00 PM")); } @@ -151,31 +206,110 @@ class test_DiagnosisModel const QNetworkInterface interface2; const QNetworkInterface interface3; QList interfaces = {interface1, interface2, interface3}; - mContext->setNetworkInterfaces(interfaces); mModel->onNetworkInfoChanged(); - QCOMPARE(mModel->mNetworkInterfaces->childCount(), 3); - for (int i = 0; i < mModel->mNetworkInterfaces->childCount(); i++) + + QCOMPARE(mModel->mNetworkInterfaceSection->mContentItems.size(), 3); + for (const auto& item : qAsConst(mModel->mNetworkInterfaceSection->mContentItems)) { - const auto& interface = mModel->mNetworkInterfaces->getChild(i); - QCOMPARE(interface->childCount(), 2); + QCOMPARE(item->mTitle, QString("Interface: \"\"")); + QCOMPARE(item->mContent, QString("Hardware address: \nNo IP addresses assigned")); } + + QCOMPARE(true, verifyOrder(mModel->mCombinedNetworkSection, {mModel->mNetworkConnectionSection, mModel->mNetworkInterfaceSection})); } void test_OnAntivirusDetectionFailed() { mModel->onAntivirusDetectionFailed(); - QCOMPARE(mModel->mInstalledAntivirus->childCount(), 1); - QCOMPARE(mModel->mInstalledAntivirus->getChild(0)->getText(), QString("Antivirus detection failed.")); + QCOMPARE(mModel->mAntivirusSection->mContentItems.size(), 1); + QCOMPARE(mModel->mAntivirusSection->mContentItems.at(0)->mTitle, QString("Antivirus information")); + QCOMPARE(mModel->mAntivirusSection->mContentItems.at(0)->mContent, QString("Antivirus detection failed.")); + QCOMPARE(true, verifyOrder(mModel->mCombinedAntivirusFirewallSection, {mModel->mAntivirusSection, mModel->mFirewallSection})); + } + + + void test_OnAntivirusInformationChanged() + { + mModel->onAntivirusInformationChanged(); + QCOMPARE(mModel->mAntivirusSection->mContentItems.size(), 1); + QCOMPARE(mModel->mAntivirusSection->mContentItems.at(0)->mTitle, QString("Antivirus information")); + QCOMPARE(mModel->mAntivirusSection->mContentItems.at(0)->mContent, QString("No Antivirus software detected.")); + QCOMPARE(true, verifyOrder(mModel->mCombinedAntivirusFirewallSection, {mModel->mAntivirusSection, mModel->mFirewallSection})); + + const QString& fileContent = getTestData(QStringLiteral("antivir_two_antivirus.txt")); + mModel->mAntivirusDetection.parseAntivirInfos(fileContent); + mModel->onAntivirusInformationChanged(); + + QCOMPARE(mModel->mAntivirusSection->mContentItems.size(), 3); + QCOMPARE(mModel->mAntivirusSection->mContentItems.at(0)->mTitle, QString("Antivirus information")); + QCOMPARE(mModel->mAntivirusSection->mContentItems.at(1)->mTitle, QString("BullGuard Antivirus")); + QCOMPARE(mModel->mAntivirusSection->mContentItems.at(1)->mContent, QString("Last updated: Fri, 30 Nov 2018 15:04:13 GMT\nExecutable path: C:\\Program Files\\BullGuard Ltd\\BullGuard\\BullGuard.exe")); + QCOMPARE(mModel->mAntivirusSection->mContentItems.at(2)->mTitle, QString("Windows Defender")); + QCOMPARE(mModel->mAntivirusSection->mContentItems.at(2)->mContent, QString("Last updated: Mon, 26 Nov 2018 10:34:23 GMT\nExecutable path: windowsdefender://")); + QCOMPARE(true, verifyOrder(mModel->mCombinedAntivirusFirewallSection, {mModel->mAntivirusSection, mModel->mFirewallSection})); } void test_OnFirewallInformationFailed() { mModel->onFirewallInformationFailed(); - QCOMPARE(mModel->mWindowsFirewall->childCount(), 1); - QCOMPARE(mModel->mWindowsFirewall->getChild(0)->getText(), QString("An error occurred while trying to gather firewall information. Please check the log for more information.")); + QCOMPARE(mModel->mFirewallSection->mContentItems.size(), 1); + QCOMPARE(mModel->mFirewallSection->mContentItems.at(0)->mTitle, QString("Firewall information")); + QCOMPARE(mModel->mFirewallSection->mContentItems.at(0)->mContent, QString("An error occurred while trying to gather firewall information. Please check the log for more information.")); + QCOMPARE(true, verifyOrder(mModel->mCombinedAntivirusFirewallSection, {mModel->mAntivirusSection, mModel->mFirewallSection})); + } + + + void test_OnConnectionTestDone_data() + { + QTest::addColumn("proxySet"); + QTest::addColumn("pingTestOnProxy"); + QTest::addColumn("connectionTestOnProxy"); + QTest::addColumn("connectionTestWithoutProxy"); + + QTest::newRow("proxyConnectionSuccessful") << true << true << true << false; + QTest::newRow("connectionWithoutProxySuccessful") << false << false << false << true; + QTest::newRow("proxyConnectionNotSuccessful") << true << false << false << false; + QTest::newRow("noConnection") << false << false << false << false; + } + + + void test_OnConnectionTestDone() + { + QFETCH(bool, proxySet); + QFETCH(bool, pingTestOnProxy); + QFETCH(bool, connectionTestOnProxy); + QFETCH(bool, connectionTestWithoutProxy); + + mModel->mConnectionTest.mIsProxySet = proxySet; + mModel->mConnectionTest.mPingTestOnProxySuccessful = pingTestOnProxy; + mModel->mConnectionTest.mConnectionTestWithProxySuccessful = connectionTestOnProxy; + mModel->mConnectionTest.mConnectionTestWithoutProxySuccessful = connectionTestWithoutProxy; + + mModel->onConnectionTestDone(); + QCOMPARE(mModel->mNetworkConnectionSection->mContentItems.size(), 1); + QCOMPARE(mModel->mNetworkConnectionSection->mContentItems.at(0)->mTitle, QString("Proxy information")); + + QString contentString; + if (proxySet) + { + contentString = QString("Hostname: \nPort: \nType: \nCapabilities: \nPing test to proxy: "); + contentString += pingTestOnProxy ? QString("Successful") : QString("Failed"); + contentString += QString("\nConnection test with proxy: "); + contentString += connectionTestOnProxy ? QString("Successful") : QString("Failed"); + } + else + { + contentString = QString("No proxy found"); + } + + contentString += QString("\nConnection test without proxy: "); + contentString += connectionTestWithoutProxy ? QString("Successful") : QString("Failed"); + + QCOMPARE(mModel->mNetworkConnectionSection->mContentItems.at(0)->mContent, contentString); + QCOMPARE(true, verifyOrder(mModel->mCombinedNetworkSection, {mModel->mNetworkConnectionSection, mModel->mNetworkInterfaceSection})); } diff --git a/test/qt/core/test_DiagnosisTreeModel.cpp b/test/qt/core/test_DiagnosisTreeModel.cpp new file mode 100644 index 0000000..53b9d18 --- /dev/null +++ b/test/qt/core/test_DiagnosisTreeModel.cpp @@ -0,0 +1,296 @@ +/*! + * \brief Unit tests for \ref DiagnosisModel + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "DiagnosisTreeModel.h" + +#include "TestFileHelper.h" + +#include + +using namespace governikus; + +class test_DiagnosisTreeModel + : public QObject +{ + Q_OBJECT + QSharedPointer mContext; + QSharedPointer mModel; + + private: + QString getTestData(const QString& pFilename) + { + QString filePath = QStringLiteral(":/core/diagnosis/") + pFilename; + QByteArray rawData = TestFileHelper::readFile(filePath); + return QString::fromUtf8(rawData); + } + + + private Q_SLOTS: + void init() + { + mContext.reset(new DiagnosisContext()); + mModel.reset(new DiagnosisTreeModel(mContext)); + } + + + void cleanup() + { + mModel.clear(); + mContext.clear(); + } + + + void test_newDiagnosisTreeModel() + { + QCOMPARE(mModel->mContext, mContext); + QCOMPARE(mModel->mRootItem->getText(), QString("Diagnosis data")); + QCOMPARE(mModel->mOperatingSystemItem->getText(), QString("Operating system")); + QCOMPARE(mModel->mReaderItem->getText(), QString("Card reader")); + QCOMPARE(mModel->mPcScItem->getText(), QString("PC/SC")); + QCOMPARE(mModel->mPairedDevices->getText(), QString("Paired devices")); + QCOMPARE(mModel->mNetworkInterfaces->getText(), QString("Network interfaces")); + QCOMPARE(mModel->mNetworkConnectionTest->getText(), QString("Network connection test")); + QCOMPARE(mModel->mInstalledAntivirus->getText(), QString("Installed antivirus software")); + QCOMPARE(mModel->mWindowsFirewall->getText(), QString("Firewall")); + QCOMPARE(mModel->mTimestampItem->getText(), QString("Time of diagnosis")); + + QCOMPARE(mModel->mRootItem->childCount(), 10); + QCOMPARE(mModel->mRootItem->getChild(0), mModel->mAppVersionItem); + QCOMPARE(mModel->mRootItem->getChild(1), mModel->mOperatingSystemItem); + QCOMPARE(mModel->mRootItem->getChild(2), mModel->mReaderItem); + QCOMPARE(mModel->mRootItem->getChild(3), mModel->mPcScItem); + QCOMPARE(mModel->mRootItem->getChild(4), mModel->mPairedDevices); + QCOMPARE(mModel->mRootItem->getChild(5), mModel->mNetworkInterfaces); + QCOMPARE(mModel->mRootItem->getChild(6), mModel->mNetworkConnectionTest); + QCOMPARE(mModel->mRootItem->getChild(7), mModel->mInstalledAntivirus); + QCOMPARE(mModel->mRootItem->getChild(8), mModel->mWindowsFirewall); + QCOMPARE(mModel->mRootItem->getChild(9), mModel->mTimestampItem); + + QCOMPARE(mModel->mOperatingSystemItem->childCount(), 3); + + QCOMPARE(mModel->mReaderItem->childCount(), 1); + QCOMPARE(mModel->mReaderItem->getChild(0)->getText(), QString("Diagnosis is running...")); + + QCOMPARE(mModel->mNetworkConnectionTest->childCount(), 1); + QCOMPARE(mModel->mNetworkConnectionTest->getChild(0)->getText(), QString("Diagnosis is running...")); + } + + + void test_OnReaderInfosChanged() + { + mModel->onReaderInfosChanged(); + QCOMPARE(mModel->mReaderItem->childCount(), 1); + QCOMPARE(mModel->mReaderItem->getChild(0)->getText(), QString("Not recognised")); + + ReaderInfo defaultInfo; + ReaderInfo infoEidCard(QString("testInfo"), ReaderManagerPlugInType::PCSC, CardInfo(CardType::EID_CARD)); + ReaderInfo comfortReaderInfo(QString("name"), ReaderManagerPlugInType::BLUETOOTH, CardInfo(CardType::UNKNOWN)); + comfortReaderInfo.setBasicReader(false); + const QVector readerInfos = {defaultInfo, infoEidCard, comfortReaderInfo}; + mContext->setReaderInfos(readerInfos); + mModel->onReaderInfosChanged(); + + QCOMPARE(mModel->mReaderItem->childCount(), 3); + QCOMPARE(mModel->mReaderItem->getChild(0)->getText(), QString()); + QCOMPARE(mModel->mReaderItem->getChild(1)->getText(), QString("testInfo")); + QCOMPARE(mModel->mReaderItem->getChild(2)->getText(), QString("name")); + + auto child1 = mModel->mReaderItem->getChild(0); + QCOMPARE(child1->childCount(), 2); + QCOMPARE(child1->getChild(0)->getText(), QString("Type: Basic card reader")); + QCOMPARE(child1->getChild(1)->getText(), QString("Card: not inserted")); + + auto child2 = mModel->mReaderItem->getChild(1); + QCOMPARE(child2->childCount(), 3); + QCOMPARE(child2->getChild(0)->getText(), QString("Type: Basic card reader")); + QCOMPARE(child2->getChild(1)->getText(), QString("Card: ID card (PA/eAT)")); + QCOMPARE(child2->getChild(2)->getText(), QString("Retry counter: -1")); + + auto child3 = mModel->mReaderItem->getChild(2); + QCOMPARE(child3->childCount(), 2); + QCOMPARE(child3->getChild(0)->getText(), QString("Type: Standard / comfort card reader")); + QCOMPARE(child3->getChild(1)->getText(), QString("Card: unknown type")); + } + + + void test_onPcscInfoChanged() + { + const QString version("version"); + const DiagnosisContext::ComponentInfo component1; + const DiagnosisContext::ComponentInfo component2; + const DiagnosisContext::ComponentInfo driver1; + const DiagnosisContext::ComponentInfo driver2; + QVector components = {component1, component2}; + QVector drivers = {driver1, driver2}; + mContext->setPcscInfo(version, components, drivers); + + mModel->onPcscInfoChanged(); + QCOMPARE(mModel->mPcScItem->childCount(), 3); + QCOMPARE(mModel->mPcScItem->getChild(0)->getText(), QString("Version: version")); + QCOMPARE(mModel->mPcScItem->getChild(1)->getText(), QString("Components")); + QCOMPARE(mModel->mPcScItem->getChild(2)->getText(), QString("Driver")); + } + + + void test_OnTimestampChanged() + { + const QDateTime invalid; + const QDate date(2018, 10, 12); + const QTime time(12, 0); + const QDateTime valid(date, time); + + mContext->setTimestamp(invalid); + mModel->onTimestampChanged(); + QCOMPARE(mModel->mTimestampItem->childCount(), 1); + QCOMPARE(mModel->mTimestampItem->getChild(0)->getText(), QString("Initial diagnosis running, please wait.")); + + mContext->setTimestamp(valid); + mModel->onTimestampChanged(); + QCOMPARE(mModel->mTimestampItem->childCount(), 1); + QCOMPARE(mModel->mTimestampItem->getChild(0)->getText(), QString("12. October 2018, 12:00:00 PM")); + } + + + void test_OnNetworkInfoChanged() + { + const QNetworkInterface interface1; + const QNetworkInterface interface2; + const QNetworkInterface interface3; + QList interfaces = {interface1, interface2, interface3}; + + mContext->setNetworkInterfaces(interfaces); + mModel->onNetworkInfoChanged(); + QCOMPARE(mModel->mNetworkInterfaces->childCount(), 3); + for (int i = 0; i < mModel->mNetworkInterfaces->childCount(); i++) + { + const auto& interface = mModel->mNetworkInterfaces->getChild(i); + QCOMPARE(interface->childCount(), 2); + } + } + + + void test_OnAntivirusDetectionFailed() + { + mModel->onAntivirusDetectionFailed(); + QCOMPARE(mModel->mInstalledAntivirus->childCount(), 1); + QCOMPARE(mModel->mInstalledAntivirus->getChild(0)->getText(), QString("Antivirus detection failed.")); + } + + + void test_OnFirewallInformationFailed() + { + mModel->onFirewallInformationFailed(); + QCOMPARE(mModel->mWindowsFirewall->childCount(), 1); + QCOMPARE(mModel->mWindowsFirewall->getChild(0)->getText(), QString("An error occurred while trying to gather firewall information. Please check the log for more information.")); + } + + + void test_OnConnectionTestDone_data() + { + QTest::addColumn("proxySet"); + QTest::addColumn("pingTestOnProxy"); + QTest::addColumn("connectionTestOnProxy"); + QTest::addColumn("connectionTestWithoutProxy"); + QTest::addColumn("childCount"); + + QTest::newRow("proxyConnectionSuccessful") << true << true << true << false << 3; + QTest::newRow("connectionWithoutProxySuccessful") << false << false << false << true << 2; + QTest::newRow("proxyConnectionNotSuccessful") << true << false << false << false << 3; + QTest::newRow("noConnection") << false << false << false << false << 2; + } + + + void test_OnConnectionTestDone() + { + QFETCH(bool, proxySet); + QFETCH(bool, pingTestOnProxy); + QFETCH(bool, connectionTestOnProxy); + QFETCH(bool, connectionTestWithoutProxy); + QFETCH(int, childCount); + + mModel->mConnectionTest.mIsProxySet = proxySet; + mModel->mConnectionTest.mPingTestOnProxySuccessful = pingTestOnProxy; + mModel->mConnectionTest.mConnectionTestWithProxySuccessful = connectionTestOnProxy; + mModel->mConnectionTest.mConnectionTestWithoutProxySuccessful = connectionTestWithoutProxy; + + auto connectionTestModelIndex = mModel->index(6, 0); + QSignalSpy spyInsert(mModel.data(), &DiagnosisTreeModel::rowsInserted); + + mModel->onConnectionTestDone(); + + if (proxySet) + { + QCOMPARE(mModel->mNetworkConnectionTest->childCount(), childCount); + + auto proxy = mModel->mNetworkConnectionTest->getChild(0); + + QCOMPARE(proxy->getText(), QString("Proxy")); + QCOMPARE(proxy->childCount(), 5); + QVERIFY(proxy->getChild(0)->getText().contains("Hostname:")); + QVERIFY(proxy->getChild(1)->getText().contains("Port:")); + QVERIFY(proxy->getChild(2)->getText().contains("Type:")); + QVERIFY(proxy->getChild(3)->getText().contains("Capabilities:")); + QCOMPARE(proxy->getChild(4)->getText(), QString("Ping test to proxy: %1").arg(pingTestOnProxy ? QString("Successful") : QString("Failed"))); + QCOMPARE(mModel->mNetworkConnectionTest->getChild(1)->getText(), QString("Connection test with proxy: %1").arg(connectionTestOnProxy ? QString("Successful") : QString("Failed"))); + QCOMPARE(spyInsert.count(), 1); + QList argumentsInsert = spyInsert.takeFirst(); + QCOMPARE(argumentsInsert.at(0), connectionTestModelIndex); + QCOMPARE(argumentsInsert.at(1).toInt(), 0); + QCOMPARE(argumentsInsert.at(2).toInt(), 2); + } + else + { + QCOMPARE(mModel->mNetworkConnectionTest->childCount(), childCount); + QCOMPARE(mModel->mNetworkConnectionTest->getChild(0)->getText(), QString("No Proxy Found")); + QCOMPARE(spyInsert.count(), 1); + QList argumentsInsert = spyInsert.takeFirst(); + QCOMPARE(argumentsInsert.at(0), connectionTestModelIndex); + QCOMPARE(argumentsInsert.at(1).toInt(), 0); + QCOMPARE(argumentsInsert.at(2).toInt(), 1); + } + QCOMPARE(mModel->mNetworkConnectionTest->getChild(childCount - 1)->getText(), QString("Connection test without proxy: %1").arg(connectionTestWithoutProxy ? QString("Successful") : QString("Failed"))); + } + + + void test_OnAntivirusInformationChanged() + { + auto installedAntivirusModelIndex = mModel->index(7, 0); + QSignalSpy spyInsert(mModel.data(), &DiagnosisTreeModel::rowsInserted); + mModel->onAntivirusInformationChanged(); + QCOMPARE(mModel->mInstalledAntivirus->childCount(), 1); + QCOMPARE(mModel->mInstalledAntivirus->getChild(0)->getText(), QString("No Antivirus software detected.")); + QCOMPARE(spyInsert.count(), 1); + QList argumentsInsert1 = spyInsert.takeFirst(); + QCOMPARE(argumentsInsert1.at(0), installedAntivirusModelIndex); + QCOMPARE(argumentsInsert1.at(1).toInt(), 0); + QCOMPARE(argumentsInsert1.at(2).toInt(), 0); + + const QString& fileContent = getTestData(QStringLiteral("antivir_two_antivirus.txt")); + mModel->mAntivirusDetection.parseAntivirInfos(fileContent); + mModel->onAntivirusInformationChanged(); + QCOMPARE(mModel->mInstalledAntivirus->childCount(), 2); + + auto antivirus1 = mModel->mInstalledAntivirus->getChild(0); + QCOMPARE(antivirus1->getText(), QString("BullGuard Antivirus")); + QCOMPARE(antivirus1->getChild(0)->getText(), QString("Last updated: Fri, 30 Nov 2018 15:04:13 GMT")); + QCOMPARE(antivirus1->getChild(1)->getText(), QString("Executable path: C:\\Program Files\\BullGuard Ltd\\BullGuard\\BullGuard.exe")); + + auto antivirus2 = mModel->mInstalledAntivirus->getChild(1); + QCOMPARE(antivirus2->getText(), QString("Windows Defender")); + QCOMPARE(antivirus2->getChild(0)->getText(), QString("Last updated: Mon, 26 Nov 2018 10:34:23 GMT")); + QCOMPARE(antivirus2->getChild(1)->getText(), QString("Executable path: windowsdefender://")); + + QList argumentsInsert2 = spyInsert.takeFirst(); + QCOMPARE(argumentsInsert2.at(0), installedAntivirusModelIndex); + QCOMPARE(argumentsInsert2.at(1).toInt(), 0); + QCOMPARE(argumentsInsert2.at(2).toInt(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_DiagnosisTreeModel) +#include "test_DiagnosisTreeModel.moc" diff --git a/test/qt/file_provider/test_Downloader.cpp b/test/qt/file_provider/test_Downloader.cpp index a627cdc..2ad3505 100644 --- a/test/qt/file_provider/test_Downloader.cpp +++ b/test/qt/file_provider/test_Downloader.cpp @@ -93,11 +93,11 @@ class test_Downloader { const QByteArray fileContent("Some icon data"); const QDateTime timestampOnServer(QDate(2017, 6, 1), QTime(12, 00, 0, 0)); - MockNetworkReply* const reply = new MockNetworkReply(fileContent, HTTP_STATUS_OK); + auto* const reply = new MockNetworkReply(fileContent, HTTP_STATUS_OK); reply->setFileModificationTimestamp(timestampOnServer); mMockNetworkManager.setNextReply(reply); - Downloader* const downloader = Env::getSingleton(); + auto* const downloader = Env::getSingleton(); QSignalSpy spy(downloader, &Downloader::fireDownloadSuccess); const QUrl url("http://server/reader/icons/icon.png"); @@ -114,7 +114,7 @@ class test_Downloader MockNetworkReply* const reply = new MockNetworkReply(QByteArray(), HTTP_STATUS_NOT_FOUND); mMockNetworkManager.setNextReply(reply); - Downloader* const downloader = Env::getSingleton(); + auto* const downloader = Env::getSingleton(); QSignalSpy spy(downloader, &Downloader::fireDownloadFailed); const QUrl url("http://server/reader/icons/icon.png"); @@ -129,12 +129,12 @@ class test_Downloader void conditionalDownloadOfNewerFile() { const QByteArray fileContent("Some icon data"); - MockNetworkReply* const reply = new MockNetworkReply(fileContent, HTTP_STATUS_OK); + auto* const reply = new MockNetworkReply(fileContent, HTTP_STATUS_OK); const QDateTime timestampOnServer(QDate(2017, 7, 1), QTime(12, 00, 0, 0)); reply->setFileModificationTimestamp(timestampOnServer); mMockNetworkManager.setNextReply(reply); - Downloader* const downloader = Env::getSingleton(); + auto* const downloader = Env::getSingleton(); QSignalSpy spy(downloader, &Downloader::fireDownloadSuccess); const QUrl url("http://server/reader/icons/icon.png"); @@ -159,7 +159,7 @@ class test_Downloader reply->setFileModificationTimestamp(timestampOnServer); mMockNetworkManager.setNextReply(reply); - Downloader* const downloader = Env::getSingleton(); + auto* const downloader = Env::getSingleton(); QSignalSpy spy(downloader, &Downloader::fireDownloadUnnecessary); const QUrl url("http://server/reader/icons/icon.png"); diff --git a/test/qt/file_provider/test_FileProvider.cpp b/test/qt/file_provider/test_FileProvider.cpp index fc1de51..ddcf126 100644 --- a/test/qt/file_provider/test_FileProvider.cpp +++ b/test/qt/file_provider/test_FileProvider.cpp @@ -4,7 +4,7 @@ #include "FileProvider.h" -#include +#include using namespace governikus; diff --git a/test/qt/file_provider/test_UpdatableFile.cpp b/test/qt/file_provider/test_UpdatableFile.cpp index 023c610..5025678 100644 --- a/test/qt/file_provider/test_UpdatableFile.cpp +++ b/test/qt/file_provider/test_UpdatableFile.cpp @@ -8,7 +8,7 @@ #include "Env.h" #include "MockDownloader.h" -#include +#include using namespace governikus; diff --git a/test/qt/global/test_CardReturnCode.cpp b/test/qt/global/test_CardReturnCode.cpp index 5e3e482..cfde5f1 100644 --- a/test/qt/global/test_CardReturnCode.cpp +++ b/test/qt/global/test_CardReturnCode.cpp @@ -43,7 +43,8 @@ class test_CardReturnCode void checkConsistency() { - for (auto returnCode : Enum::getList()) + const auto list = Enum::getList(); + for (auto returnCode : list) { QVERIFY(!CardReturnCodeUtil::toGlobalStatus(returnCode).toErrorDescription().isEmpty()); } diff --git a/test/qt/global/test_ECardApiResult.cpp b/test/qt/global/test_ECardApiResult.cpp index 2f1423c..84c91f1 100644 --- a/test/qt/global/test_ECardApiResult.cpp +++ b/test/qt/global/test_ECardApiResult.cpp @@ -7,7 +7,7 @@ #include "LogHandler.h" #include -#include +#include using namespace governikus; @@ -152,7 +152,7 @@ class test_ECardApiResult const QMetaEnum& metaEnum = QMetaEnum::fromType(); for (int i = 0; i < metaEnum.keyCount(); i++) { - const ECardApiResult::Minor minor = static_cast(i); + const auto minor = static_cast(i); const char* name = metaEnum.valueToKey(i); if (minor == ECardApiResult::Minor::null) @@ -180,7 +180,7 @@ class test_ECardApiResult const QMetaEnum& metaEnum = QMetaEnum::fromType(); for (int i = 0; i < metaEnum.keyCount(); i++) { - const ECardApiResult::Minor minor = static_cast(i); + const auto minor = static_cast(i); const char* name = metaEnum.valueToKey(i); if (minor == ECardApiResult::Minor::null) @@ -261,7 +261,7 @@ class test_ECardApiResult const QMetaEnum& metaEnum = QMetaEnum::fromType(); for (int i = 0; i < metaEnum.keyCount(); i++) { - const GlobalStatus::Code statusCode = static_cast(i); + const auto statusCode = static_cast(i); const char* name = metaEnum.valueToKey(i); QTest::newRow(name) << statusCode; } diff --git a/test/qt/global/test_EnumHelper.cpp b/test/qt/global/test_EnumHelper.cpp index 7f5f6b4..43d0160 100644 --- a/test/qt/global/test_EnumHelper.cpp +++ b/test/qt/global/test_EnumHelper.cpp @@ -38,7 +38,7 @@ class test_EnumHelper { QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); - TestEnum1 badEnumValue = static_cast(pValue); + auto badEnumValue = static_cast(pValue); QCOMPARE(Enum::getName(badEnumValue), QLatin1String()); QCOMPARE(spy.count(), 1); @@ -120,7 +120,6 @@ class test_EnumHelper QCOMPARE(list2.size(), 3); QCOMPARE(list1, list2); - QVERIFY(&list1 == &list2); QCOMPARE(list1[0], TestEnum2::FIRST); QCOMPARE(list1[1], TestEnum2::SECOND); diff --git a/test/qt/global/test_FileDestination.cpp b/test/qt/global/test_FileDestination.cpp index 3bfe51c..478eaba 100644 --- a/test/qt/global/test_FileDestination.cpp +++ b/test/qt/global/test_FileDestination.cpp @@ -17,18 +17,11 @@ class test_FileDestination Q_OBJECT private Q_SLOTS: - void getPathCharPtr() - { - QString path = FileDestination::getPath("qtlogging.ini"); - QVERIFY(path.endsWith("qtlogging.ini")); - QVERIFY(QFile::exists(path)); - } - - void getPath() { - QString path = FileDestination::getPath(QStringLiteral("qtlogging.ini")); - QVERIFY(path.endsWith("qtlogging.ini")); + const auto filename = QStringLiteral("config.json"); + QString path = FileDestination::getPath(filename); + QVERIFY(path.endsWith(filename)); QVERIFY(QFile::exists(path)); } diff --git a/test/qt/global/test_GlobalStatus.cpp b/test/qt/global/test_GlobalStatus.cpp index cb04b94..74d0cd3 100644 --- a/test/qt/global/test_GlobalStatus.cpp +++ b/test/qt/global/test_GlobalStatus.cpp @@ -46,8 +46,8 @@ class test_GlobalStatus QTest::addColumn("message"); QTest::newRow("inProgress") << GlobalStatus::Code::Workflow_AlreadyInProgress_Error << tr("Cannot start authentication. An operation is already in progress."); - QTest::newRow("cardRemoved") << GlobalStatus::Code::Workflow_Card_Removed << tr("The ID card has been removed. The process is aborted."); - QTest::newRow("unknownPaos") << GlobalStatus::Code::Workflow_Unknown_Paos_Form_EidServer << tr("The program received an unknown message from the server."); + QTest::newRow("cardRemoved") << GlobalStatus::Code::Workflow_Card_Removed << tr("The connection to the ID card has been lost. The process was aborted."); + QTest::newRow("unknownPaos") << GlobalStatus::Code::Workflow_Unknown_Paos_From_EidServer << tr("The program received an unknown message from the server."); QTest::newRow("unexpectedMessage") << GlobalStatus::Code::Workflow_Unexpected_Message_From_EidServer << tr("The program received an unexpected message from the server."); QTest::newRow("pinBlocked") << GlobalStatus::Code::Workflow_Pin_Blocked_And_Puk_Objectionable << tr("After three wrong entries your PIN is blocked. Please use the PIN management in this app to unblock it with the help of your PUK."); QTest::newRow("preverificationDevelopermode") << GlobalStatus::Code::Workflow_Preverification_Developermode_Error << tr("Using the developer mode is only allowed in a test environment."); @@ -59,15 +59,11 @@ class test_GlobalStatus QTest::newRow("hashError") << GlobalStatus::Code::Workflow_Certificate_Hash_Error << tr("The certificate description does not match the certificate."); QTest::newRow("certificateSop") << GlobalStatus::Code::Workflow_Certificate_Sop_Error << tr("The subject URL in the certificate description and the TCToken URL don't satisfy the same origin policy."); QTest::newRow("wrongParameter") << GlobalStatus::Code::Workflow_Wrong_Parameter_Invocation << tr("Application was invoked with wrong parameters."); - QTest::newRow("readerCommunication") << GlobalStatus::Code::Workflow_Reader_Communication_Error << tr("An error occurred while communicating with the card reader."); QTest::newRow("incompleteInformation") << GlobalStatus::Code::Workflow_Server_Incomplete_Information_Provided << tr("The server provided no or incomplete information. Your personal data could not be read out."); - QTest::newRow("deviceConnection") << GlobalStatus::Code::Workflow_Reader_Device_Connection_Error << tr("An error occurred while connecting to a reader device."); + QTest::newRow("deviceConnection") << GlobalStatus::Code::Workflow_Bluetooth_Reader_Connection_Error << tr("An error occurred while connecting to a reader device."); QTest::newRow("deviceScan") << GlobalStatus::Code::Workflow_Reader_Device_Scan_Error << tr("An error occurred while scanning for reader devices."); - QTest::newRow("normalClose") << GlobalStatus::Code::RemoteReader_CloseCode_NormalClose << tr("The remote reader connection was closed properly."); QTest::newRow("abnormalClose") << GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose << tr("The remote card reader connection was not closed properly."); - QTest::newRow("closeUndefined") << GlobalStatus::Code::RemoteReader_CloseCode_Undefined << tr("Undefined error code occured when the remote card reader connection was closed."); QTest::newRow("invalidRequest") << GlobalStatus::Code::RemoteConnector_InvalidRequest << tr("Remote reader connection request contains invalid parameters."); - QTest::newRow("emptyPassword") << GlobalStatus::Code::RemoteConnector_EmptyPassword << tr("Empty password in extended encryption of remote reader connection request."); QTest::newRow("noSupportedApiLevel") << GlobalStatus::Code::RemoteConnector_NoSupportedApiLevel << tr("Your remote reader version is incompatible with the local version. Please install the latest AusweisApp2 version on both your smartphone and your computer."); QTest::newRow("connectionTimeout") << GlobalStatus::Code::RemoteConnector_ConnectionTimeout << tr("A timeout occurred while trying to establish a connection to a remote reader."); QTest::newRow("connectionError") << GlobalStatus::Code::RemoteConnector_ConnectionError << tr("An error occurred while trying to establish a connection to a remote reader."); diff --git a/test/qt/global/test_Initializer.cpp b/test/qt/global/test_Initializer.cpp index 2a800cb..2c0a400 100644 --- a/test/qt/global/test_Initializer.cpp +++ b/test/qt/global/test_Initializer.cpp @@ -6,7 +6,7 @@ #include "Initializer.h" -#include +#include using namespace governikus; diff --git a/test/qt/global/test_LogHandler.cpp b/test/qt/global/test_LogHandler.cpp index 605a2bf..4cb22d2 100644 --- a/test/qt/global/test_LogHandler.cpp +++ b/test/qt/global/test_LogHandler.cpp @@ -28,7 +28,7 @@ class test_LogHandler void fakeLastModifiedAndLastAccessTime(const QString& pPath) { #ifdef Q_OS_WIN - Q_UNUSED(pPath); + Q_UNUSED(pPath) #else struct timeval tv[2]; @@ -84,6 +84,10 @@ class test_LogHandler Env::getSingleton()->resetBacklog(); blog = Env::getSingleton()->getBacklog(); QCOMPARE(blog.size(), 0); + + blog = Env::getSingleton()->getBacklog(true); + QVERIFY(blog.size() > 0); + QVERIFY(blog.contains(msg)); } diff --git a/test/qt/global/test_ResourceLoader.cpp b/test/qt/global/test_ResourceLoader.cpp index 005ed00..2514c7c 100644 --- a/test/qt/global/test_ResourceLoader.cpp +++ b/test/qt/global/test_ResourceLoader.cpp @@ -8,7 +8,7 @@ #include "LogHandler.h" -#include +#include using namespace governikus; diff --git a/test/qt/global/test_ScopeGuard.cpp b/test/qt/global/test_ScopeGuard.cpp index fcbb97b..63fe458 100644 --- a/test/qt/global/test_ScopeGuard.cpp +++ b/test/qt/global/test_ScopeGuard.cpp @@ -10,6 +10,8 @@ #include +QT_WARNING_DISABLE_DEPRECATED + using namespace governikus; class test_ScopeGuard diff --git a/test/qt/global/test_VersionInfo.cpp b/test/qt/global/test_VersionInfo.cpp index 20afa30..75452eb 100644 --- a/test/qt/global/test_VersionInfo.cpp +++ b/test/qt/global/test_VersionInfo.cpp @@ -2,8 +2,8 @@ * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany */ -#include -#include +#include +#include #include "LogHandler.h" #include "VersionInfo.h" diff --git a/test/qt/global/test_VersionNumber.cpp b/test/qt/global/test_VersionNumber.cpp index 879da93..7a3d9fb 100644 --- a/test/qt/global/test_VersionNumber.cpp +++ b/test/qt/global/test_VersionNumber.cpp @@ -6,7 +6,7 @@ #include "VersionNumber.h" -#include +#include using namespace governikus; @@ -72,114 +72,115 @@ class test_VersionNumber } + void distance_data() + { + QTest::addColumn("distance"); + + QTest::newRow("1.99+432") << 432; + QTest::newRow("1.99+98-default-et43t") << 98; + QTest::newRow("1.99+1-stable") << 1; + QTest::newRow("1.91.2.2.2.2") << -1; + QTest::newRow("1.91.2.7+534533") << 534533; + QTest::newRow("1.91.2.7+12++4243+2+2-default") << -1; + QTest::newRow("1.91.2.7+12++4243+2+2default") << -1; + } + + void distance() { - VersionNumber number1("1.99+432"); - QCOMPARE(number1.getDistance(), 432); + QFETCH(int, distance); - VersionNumber number2("1.99+98-default-et43t"); - QCOMPARE(number2.getDistance(), 98); + const auto& version = QString::fromLatin1(QTest::currentDataTag()); + QCOMPARE(VersionNumber(version).getDistance(), distance); + } - VersionNumber number3("1.99+1-stable"); - QCOMPARE(number3.getDistance(), 1); - VersionNumber number4("1.91.2.2.2.2"); - QCOMPARE(number4.getDistance(), -1); + void branch_data() + { + QTest::addColumn("branch"); - VersionNumber number5("1.91.2.7+534533"); - QCOMPARE(number5.getDistance(), 534533); - - VersionNumber number6("1.91.2.7+12++4243+2+2-default"); - QCOMPARE(number6.getDistance(), -1); - - VersionNumber number7("1.91.2.7+12++4243+2+2default"); - QCOMPARE(number7.getDistance(), -1); + QTest::newRow("1.99+432") << QString(); + QTest::newRow("1.99+98-default-et43t") << QString("default"); + QTest::newRow("1.99+1-stable") << QString("stable"); + QTest::newRow("1.99+98-default-et43t+") << QString("default"); + QTest::newRow("1.99+98-default-draft-et43t+") << QString("default"); } void branch() { - VersionNumber number1("1.99+432"); - QCOMPARE(number1.getBranch(), QString()); + QFETCH(QString, branch); - VersionNumber number2("1.99+98-default-et43t"); - QCOMPARE(number2.getBranch(), QString("default")); + const auto& version = QString::fromLatin1(QTest::currentDataTag()); + QCOMPARE(VersionNumber(version).getBranch(), branch); + } - VersionNumber number3("1.99+1-stable"); - QCOMPARE(number3.getBranch(), QString("stable")); - VersionNumber number4("1.99+98-default-et43t+"); - QCOMPARE(number4.getBranch(), QString("default")); + void revision_data() + { + QTest::addColumn("revision"); - VersionNumber number5("1.99+98-default-draft-et43t+"); - QCOMPARE(number5.getBranch(), QString("default")); + QTest::newRow("1.99+432") << QString(); + QTest::newRow("1.99+98-default-et43t") << QString("et43t"); + QTest::newRow("1.99+1-stable") << QString(); + QTest::newRow("1.99+98-default-draft-et43t+") << QString("et43t+"); } void revision() { - VersionNumber number1("1.99+432"); - QCOMPARE(number1.getRevision(), QString()); + QFETCH(QString, revision); - VersionNumber number2("1.99+98-default-et43t"); - QCOMPARE(number2.getRevision(), QString("et43t")); + const auto& version = QString::fromLatin1(QTest::currentDataTag()); + QCOMPARE(VersionNumber(version).getRevision(), revision); + } - VersionNumber number3("1.99+1-stable"); - QCOMPARE(number3.getRevision(), QString()); - VersionNumber number4("1.99+98-default-draft-et43t+"); - QCOMPARE(number4.getRevision(), QString("et43t+")); + void isDraft_data() + { + QTest::addColumn("draft"); + + QTest::newRow("1.5.0+16-default-secret") << true; + QTest::newRow("1.6.0+1-draft-t34t53+") << true; + QTest::newRow("1.6.0+12-stable-abc123") << false; } void isDraft() { - VersionNumber number1("1.5.0+16-default-secret"); - QVERIFY(number1.isDraft()); + QFETCH(bool, draft); - VersionNumber number2("1.6.0+1-draft-t34t53+"); - QVERIFY(number2.isDraft()); + const auto& version = QString::fromLatin1(QTest::currentDataTag()); + QCOMPARE(VersionNumber(version).isDraft(), draft); + } - VersionNumber number3("1.6.0+12-stable-abc123"); - QVERIFY(!number3.isDraft()); + + void isDeveloper_data() + { + QTest::addColumn("developer"); + + QTest::newRow("") << true; + QTest::newRow("1.5.0+16-default-secret") << true; + QTest::newRow("1.6.0+1-draft-t34t53+") << true; + QTest::newRow("1.5.0") << true; + QTest::newRow("1.6.0") << false; + QTest::newRow("1.5.0+0") << true; + QTest::newRow("1.6.0+0") << true; + QTest::newRow("1.6.0+422312-stable-2143eg435") << true; + QTest::newRow("1.9.0+422312-stable-2143eg435") << true; + QTest::newRow("3.28.1") << false; + QTest::newRow("3.28.1+23-default") << true; + QTest::newRow(" 3.28.1+23-default ") << true; + QTest::newRow(" 1.10.0 ") << false; } void isDeveloper() { - QString empty; - VersionNumber number0(empty); - QVERIFY(number0.isDeveloperVersion()); + QFETCH(bool, developer); - VersionNumber number1("1.5.0"); - QVERIFY(number1.isDeveloperVersion()); - - VersionNumber number2("1.6.0"); - QVERIFY(!number2.isDeveloperVersion()); - - VersionNumber number3("1.5.0+0"); - QVERIFY(number3.isDeveloperVersion()); - - VersionNumber number4("1.6.0+0"); - QVERIFY(number4.isDeveloperVersion()); - - VersionNumber number5("1.6.0+422312-stable-2143eg435"); - QVERIFY(number5.isDeveloperVersion()); - - VersionNumber number6("1.9.0+422312-stable-2143eg435"); - QVERIFY(number6.isDeveloperVersion()); - - VersionNumber number7("3.28.1"); - QVERIFY(!number7.isDeveloperVersion()); - - VersionNumber number8("3.28.1+23-default"); - QVERIFY(number8.isDeveloperVersion()); - - VersionNumber number9(" 3.28.1+23-default "); - QVERIFY(number9.isDeveloperVersion()); - - VersionNumber number10(" 1.10.0 "); - QVERIFY(!number10.isDeveloperVersion()); + const auto& version = QString::fromLatin1(QTest::currentDataTag()); + QCOMPARE(VersionNumber(version).isDeveloperVersion(), developer); } diff --git a/test/qt/network/test_DatagramHandlerImpl.cpp b/test/qt/network/test_DatagramHandlerImpl.cpp index 23e8988..abc0adc 100644 --- a/test/qt/network/test_DatagramHandlerImpl.cpp +++ b/test/qt/network/test_DatagramHandlerImpl.cpp @@ -12,6 +12,9 @@ #include #include #include +#ifdef Q_OS_MACOS +#include +#endif using namespace governikus; @@ -63,6 +66,13 @@ class test_DatagramHandlerImpl QSKIP("Windows does not block privileged ports"); #endif + #ifdef Q_OS_MACOS + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 14)) + { + QSKIP("macOS >= 10.14 does not block privileged ports - https://news.ycombinator.com/item?id=18302380"); + } + #endif + DatagramHandlerImpl::cPort = 80; QSignalSpy spy(Env::getSingleton(), &LogHandler::fireLog); @@ -168,11 +178,11 @@ class test_DatagramHandlerImpl #ifdef Q_OS_FREEBSD QSKIP("FreeBSD does not like that"); #endif - QVERIFY(datagramHandlerImpl->send(doc.toJson(QJsonDocument::Compact), receiver.localPort())); + QVERIFY(datagramHandlerImpl->sendToAllAddressEntries(doc.toJson(QJsonDocument::Compact), receiver.localPort())); } else { - QVERIFY(datagramHandlerImpl->send(doc.toJson(QJsonDocument::Compact), QHostAddress::LocalHost, receiver.localPort())); + QVERIFY(datagramHandlerImpl->sendToAddress(doc.toJson(QJsonDocument::Compact), QHostAddress::LocalHost, receiver.localPort())); } QTRY_COMPARE(spyReceiver.count(), 1); diff --git a/test/qt/network/test_HttpRequest.cpp b/test/qt/network/test_HttpRequest.cpp index f612cfc..74f697e 100644 --- a/test/qt/network/test_HttpRequest.cpp +++ b/test/qt/network/test_HttpRequest.cpp @@ -21,7 +21,7 @@ class test_HttpRequest private Q_SLOTS: void parseEmptyBody() { - MockSocket* socket = new MockSocket; + auto* socket = new MockSocket; socket->mReadBuffer = QByteArray("GET /favicon.ico HTTP/1.1\r\n" "Host: Dummy.de\r\n" "\r\n\r\n"); @@ -38,7 +38,7 @@ class test_HttpRequest void isUpgrade() { - MockSocket* socket = new MockSocket; + auto* socket = new MockSocket; socket->mReadBuffer = QByteArray("GET / HTTP/1.1\r\n" "Host: server.example.com\r\n" "Upgrade: websocket\r\n" @@ -63,7 +63,7 @@ class test_HttpRequest void tcTokenURL() { - MockSocket* socket = new MockSocket; + auto* socket = new MockSocket; socket->mReadBuffer = QByteArray("GET /eID-Client?tcTokenURL=https%3A%2F%2Ftest.governikus-eid.de%3A443%2FAutent-DemoApplication%2FRequestServlet%3Fprovider%3Ddemo_epa_20%26redirect%3Dtrue HTTP/1.1\r\n" "Host: 127.0.0.1:24727\r\n" "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0\r\n" diff --git a/test/qt/network/test_NetworkManager.cpp b/test/qt/network/test_NetworkManager.cpp index 527c33d..6f3ce43 100644 --- a/test/qt/network/test_NetworkManager.cpp +++ b/test/qt/network/test_NetworkManager.cpp @@ -50,7 +50,7 @@ class test_NetworkManager QCOMPARE(reply->request(), request); QCOMPARE(request.sslConfiguration().ellipticCurves().size(), 6); QVERIFY(request.sslConfiguration().ellipticCurves().contains(QSslEllipticCurve::fromLongName("prime256v1"))); - const int cipherCount = SecureStorage::getInstance().getTlsConfig().getCiphers().size(); + const int cipherCount = Env::getSingleton()->getTlsConfig().getCiphers().size(); QCOMPARE(request.sslConfiguration().ciphers().size(), cipherCount); QVERIFY(request.sslConfiguration().ciphers().contains(QSslCipher("ECDHE-RSA-AES256-GCM-SHA384"))); } @@ -64,7 +64,7 @@ class test_NetworkManager QCOMPARE(request.rawHeader("PAOS"), QByteArray("ver=\"paosNamespace\"")); QCOMPARE(reply->request(), request); QCOMPARE(request.sslConfiguration().ellipticCurves().size(), 0); - const int cipherCount = SecureStorage::getInstance().getTlsConfig(SecureStorage::TlsSuite::PSK).getCiphers().size(); + const int cipherCount = Env::getSingleton()->getTlsConfig(SecureStorage::TlsSuite::PSK).getCiphers().size(); QCOMPARE(request.sslConfiguration().ciphers().size(), cipherCount); QVERIFY(request.sslConfiguration().ciphers().contains(QSslCipher("RSA-PSK-AES128-CBC-SHA256"))); QVERIFY(request.sslConfiguration().ciphers().contains(QSslCipher("RSA-PSK-AES128-GCM-SHA256"))); diff --git a/test/qt/network/test_TlsChecker.cpp b/test/qt/network/test_TlsChecker.cpp index 6cc91aa..cd21a61 100644 --- a/test/qt/network/test_TlsChecker.cpp +++ b/test/qt/network/test_TlsChecker.cpp @@ -42,7 +42,7 @@ class test_TlsChecker void initTestCase() { Env::getSingleton()->init(); - certs = SecureStorage::getInstance().getUpdateCertificates(); + certs = Env::getSingleton()->getUpdateCertificates(); QVERIFY(certs.size() > 0); } diff --git a/test/qt/qml/test_AuthModel.cpp b/test/qt/qml/test_AuthModel.cpp deleted file mode 100644 index bafab38..0000000 --- a/test/qt/qml/test_AuthModel.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/*! - * \brief Unit tests for \ref AuthModel - * - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "AuthModel.h" - -#include "context/AuthContext.h" - -#include "MockActivationContext.h" - -#include -#include - - -using namespace governikus; - - -class test_AuthModel - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void test_ResetContext() - { - AuthModel model; - const QSharedPointer context(new AuthContext(nullptr)); - const QString transactionInfo = QStringLiteral("info"); - - QSignalSpy spyCurrentStateChanged(&model, &WorkflowModel::fireCurrentStateChanged); - QSignalSpy spyTransactionInfoChanged(&model, &AuthModel::fireTransactionInfoChanged); - - model.resetContext(nullptr); - QCOMPARE(spyCurrentStateChanged.count(), 1); - QCOMPARE(spyTransactionInfoChanged.count(), 0); - - model.mTransactionInfo = transactionInfo; - model.resetContext(context); - QVERIFY(model.mTransactionInfo.isEmpty()); - QCOMPARE(spyCurrentStateChanged.count(), 2); - QCOMPARE(spyTransactionInfoChanged.count(), 1); - } - - - void test_OnDidAuthenticateEac1Changed() - { - AuthModel model; - const QSharedPointer context(new AuthContext(nullptr)); - const QString transactionInfo = QStringLiteral("info"); - const QSharedPointer eac1(new DIDAuthenticateEAC1()); - Eac1InputType type; - const QString info = QStringLiteral("new info"); - QSignalSpy spy(&model, &AuthModel::fireTransactionInfoChanged); - - model.mContext = context; - model.mTransactionInfo = transactionInfo; - model.onDidAuthenticateEac1Changed(); - QCOMPARE(model.mTransactionInfo, QString()); - QCOMPARE(spy.count(), 1); - - type.setTransactionInfo(info); - eac1->setEac1InputType(type); - context->setDidAuthenticateEac1(eac1); - model.onDidAuthenticateEac1Changed(); - QCOMPARE(model.mTransactionInfo, info); - QCOMPARE(spy.count(), 2); - } - - -}; - -QTEST_GUILESS_MAIN(test_AuthModel) -#include "test_AuthModel.moc" diff --git a/test/qt/qml/test_ChatModel.cpp b/test/qt/qml/test_ChatModel.cpp deleted file mode 100644 index e9a8810..0000000 --- a/test/qt/qml/test_ChatModel.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/*! - * \brief Unit tests for \ref ChatModel - * - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "ChatModel.h" - -#include "context/SelfAuthContext.h" - -#include "MockActivationContext.h" - -#include -#include - - -using namespace governikus; - - -class test_ChatModel - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void test_ResetContext() - { - ChatModel model; - QSharedPointer activationContext(new MockActivationContext()); - QSharedPointer authContext(new AuthContext(activationContext)); - QSharedPointer selfAuthContext(new SelfAuthContext()); - - model.resetContext(nullptr); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG05)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG13)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG04)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG07)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG08)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG09)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG17)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG10)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG06)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG02)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG19)); - QCOMPARE(model.mSelectedRights, model.mAllRights.toSet()); - - model.resetContext(selfAuthContext); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG05)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG13)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG04)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG07)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG08)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG09)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG17)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG10)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG06)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG02)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG19)); - QCOMPARE(model.mSelectedRights, model.mAllRights.toSet()); - - model.resetContext(authContext); - QVERIFY(model.mAllRights.isEmpty()); - QVERIFY(model.mOptionalRights.isEmpty()); - QVERIFY(model.mSelectedRights.isEmpty()); - } - - - void test_OnAuthenticationDataChanged() - { - ChatModel model; - QSharedPointer activationContext(new MockActivationContext()); - QSharedPointer authContext(new AuthContext(activationContext)); - model.mAuthContext = authContext; - - model.onAuthenticationDataChanged(); - QVERIFY(model.mAllRights.isEmpty()); - QVERIFY(model.mOptionalRights.isEmpty()); - QVERIFY(model.mSelectedRights.isEmpty()); - - authContext->mRequiredAccessRights += AccessRight::READ_DG01; - authContext->mRequiredAccessRights += AccessRight::READ_DG04; - - model.onAuthenticationDataChanged(); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG04)); - QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG01)); - QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG04)); - QVERIFY(model.mOptionalRights.isEmpty()); - - authContext->mOptionalAccessRights += AccessRight::READ_DG10; - authContext->mOptionalAccessRights += AccessRight::READ_DG17; - - model.onAuthenticationDataChanged(); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG04)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG10)); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG17)); - QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG01)); - QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG04)); - QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG10)); - QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG17)); - QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG10)); - QVERIFY(model.mSelectedRights.contains(AccessRight::READ_DG17)); - } - - - void test_SetOrderedAllRights() - { - ChatModel model; - QSet rights; - rights.insert(AccessRight::READ_DG01); - rights.insert(AccessRight::INSTALL_QUAL_CERT); - rights.insert(AccessRight::CAN_ALLOWED); - model.setOrderedAllRights(rights); - QVERIFY(model.mAllRights.contains(AccessRight::READ_DG01)); - QVERIFY(model.mAllRights.contains(AccessRight::INSTALL_QUAL_CERT)); - QVERIFY(!model.mAllRights.contains(AccessRight::CAN_ALLOWED)); - } - - - void test_TransferAccessRights() - { - ChatModel model; - QSharedPointer activationContext(new MockActivationContext()); - QSharedPointer authContext(new AuthContext(activationContext)); - authContext->mOptionalAccessRights += model.mSelectedRights += AccessRight::INSTALL_QUAL_CERT; - authContext->mOptionalAccessRights += model.mSelectedRights += AccessRight::READ_DG01; - QVERIFY(!authContext->getEffectiveAccessRights().contains(AccessRight::INSTALL_QUAL_CERT)); - QVERIFY(!authContext->getEffectiveAccessRights().contains(AccessRight::READ_DG01)); - - model.mAuthContext = authContext; - model.mSelectedRights += AccessRight::INSTALL_QUAL_CERT; - model.mSelectedRights += AccessRight::READ_DG01; - - model.transferAccessRights(); - QVERIFY(authContext->getEffectiveAccessRights().contains(AccessRight::INSTALL_QUAL_CERT)); - QVERIFY(authContext->getEffectiveAccessRights().contains(AccessRight::READ_DG01)); - } - - -}; - -QTEST_GUILESS_MAIN(test_ChatModel) -#include "test_ChatModel.moc" diff --git a/test/qt/qml/test_LogModel.cpp b/test/qt/qml/test_LogModel.cpp deleted file mode 100644 index e3c620f..0000000 --- a/test/qt/qml/test_LogModel.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/*! - * \brief Unit tests for \ref LogModel - * - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "LogModel.h" - -#include "LogHandler.h" - -#include -#include - - -using namespace governikus; - -class test_LogModel - : public QObject -{ - Q_OBJECT - QSharedPointer mModel; - - private Q_SLOTS: - void init() - { - mModel.reset(new LogModel()); - } - - - void cleanup() - { - mModel.clear(); - } - - - void test_AddLogEntry_data() - { - QTest::addColumn("input"); - QTest::addColumn("entry1"); - QTest::addColumn("entry2"); - - QTest::newRow("validEntry") << QString("input : test") << QString("input") << QString("test"); - QTest::newRow("empty") << QString(" : ") << QString() << QString(); - QTest::newRow("leftEmpty") << QString(" : test") << QString() << QString("test"); - QTest::newRow("rightEmpty") << QString("input : ") << QString("input") << QString(); - QTest::newRow("NoSpaceRight") << QString("input :test") << QString("input :test") << QString(); - QTest::newRow("NoSpaceLeft") << QString("input: test") << QString("input: test") << QString(); - QTest::newRow("invalidEntry") << QString("inputTest") << QString("inputTest") << QString(); - QTest::newRow("NoSpaces") << QString("input:test") << QString("input:test") << QString(); - QTest::newRow("emptyString") << QString() << QString() << QString(); - QTest::newRow("::") << QString("::") << QString("::") << QString(); - QTest::newRow("a : b : c") << QString("a : b : c") << QString("a") << QString("b : c"); - } - - - void test_AddLogEntry() - { - QFETCH(QString, input); - QFETCH(QString, entry1); - QFETCH(QString, entry2); - - mModel->addLogEntry(input); - QCOMPARE(mModel->mLogEntries.at(0), entry1); - QCOMPARE(mModel->mLogEntries.at(1), entry2); - } - - - void test_MoveViewNoChange_data() - { - QTest::addColumn("fileName"); - - QTest::newRow("Empty") << QString(":/logfiles/empty.txt"); - QTest::newRow("Size78") << QString(":/logfiles/size78.txt"); - QTest::newRow("Size80") << QString(":/logfiles/size80.txt"); - } - - - void test_MoveViewNoChange() - { - QFETCH(QString, fileName); - QFile file(fileName); - file.open(QIODevice::ReadOnly); - QTextStream stream(&file); - mModel->setLogEntries(stream); - - QSignalSpy spyRemove(mModel.data(), &LogModel::rowsRemoved); - QSignalSpy spyInsert(mModel.data(), &LogModel::rowsInserted); - QSignalSpy spyVisibleAreaChanged(mModel.data(), &LogModel::fireVisibleAreaChanged); - - for (int i = -5; i <= 5; i++) - { - mModel->moveView(i); - QCOMPARE(spyRemove.count(), 0); - QCOMPARE(spyInsert.count(), 0); - QCOMPARE(spyVisibleAreaChanged.count(), 0); - } - } - - - void test_MoveView_data() - { - QTest::addColumn("fileName"); - QTest::addColumn("input"); - QTest::addColumn("index"); - QTest::addColumn("rowsRemovedStart"); - QTest::addColumn("rowsRemovedEnd"); - QTest::addColumn("rowsInsertedStart"); - QTest::addColumn("rowsInsertedEnd"); - QTest::addColumn("newIndex"); - QTest::addColumn("dataNewIndex"); - - QTest::newRow("ScrollUpSuccessful_Size82") << QString(":/logfiles/size82.txt") << -2 << 2 << 78 << 79 << 0 << 1 << 0 << QString("1 input"); - QTest::newRow("ScrollDownSuccessful_Size82") << QString(":/logfiles/size82.txt") << 1 << 0 << 0 << 0 << 79 << 79 << 1 << QString("1 test"); - QTest::newRow("ScrollUp_DistanceOutOfRange_Size82") << QString(":/logfiles/size82.txt") << -50 << 20 << 60 << 79 << 0 << 19 << 0 << QString("1 input"); - QTest::newRow("ScrollDown_DistanceOutOfRange_Size82") << QString(":/logfiles/size82.txt") << 50 << 70 << 12 << 79 << 0 << 67 << 2 << QString("2 input"); - - QTest::newRow("ScrollUpSuccessful_Size160") << QString(":/logfiles/size160.txt") << -20 << 50 << 60 << 79 << 0 << 19 << 30 << QString("16 input"); - QTest::newRow("ScrollDownSuccessful_Size160") << QString(":/logfiles/size160.txt") << 25 << 0 << 0 << 24 << 55 << 79 << 25 << QString("13 test"); - QTest::newRow("ScrollUp_DistanceOutOfRange_Size160") << QString(":/logfiles/size160.txt") << -41 << 40 << 40 << 79 << 0 << 39 << 0 << QString("1 input"); - QTest::newRow("ScrollDown_DistanceOutOfRange_Size160") << QString(":/logfiles/size160.txt") << 82 << 79 << 0 << 0 << 79 << 79 << 80 << QString("41 input"); - } - - - void test_MoveView() - { - QFETCH(QString, fileName); - QFETCH(int, index); - QFETCH(int, input); - QFETCH(int, rowsRemovedStart); - QFETCH(int, rowsRemovedEnd); - QFETCH(int, rowsInsertedStart); - QFETCH(int, rowsInsertedEnd); - QFETCH(int, newIndex); - QFETCH(QString, dataNewIndex); - - QFile file(fileName); - file.open(QIODevice::ReadOnly); - QTextStream stream(&file); - mModel->setLogEntries(stream); - mModel->mIndex = index; - QSignalSpy spyRemove(mModel.data(), &LogModel::rowsRemoved); - QSignalSpy spyInsert(mModel.data(), &LogModel::rowsInserted); - QSignalSpy spyVisibleAreaChanged(mModel.data(), &LogModel::fireVisibleAreaChanged); - - mModel->moveView(input); - QCOMPARE(spyRemove.count(), 1); - QCOMPARE(spyInsert.count(), 1); - QCOMPARE(spyVisibleAreaChanged.count(), 1); - QList argumentsRemove = spyRemove.takeFirst(); - QCOMPARE(argumentsRemove.at(1).toInt(), rowsRemovedStart); - QCOMPARE(argumentsRemove.at(2).toInt(), rowsRemovedEnd); - QList argumentsInsert = spyInsert.takeFirst(); - QCOMPARE(argumentsInsert.at(1).toInt(), rowsInsertedStart); - QCOMPARE(argumentsInsert.at(2).toInt(), rowsInsertedEnd); - QCOMPARE(mModel->mIndex, newIndex); - - if (!mModel->mLogEntries.isEmpty() && !mModel->mLogEntries.at(newIndex).isNull()) - { - QModelIndex indexBegin = mModel->createIndex(0, 0); - QCOMPARE(mModel->data(indexBegin, 0), QVariant(dataNewIndex)); - } - } - - - void test_SetLogEntries_data() - { - QTest::addColumn("fileName"); - QTest::addColumn("logEntriesSize"); - QTest::addColumn("count"); - - QTest::newRow("empty") << QString(":/logfiles/empty.txt") << 0 << 0; - QTest::newRow("size78") << QString(":/logfiles/size78.txt") << 78 << 78; - QTest::newRow("size80") << QString(":/logfiles/size80.txt") << 80 << 80; - QTest::newRow("size82") << QString(":/logfiles/size82.txt") << 82 << 80; - QTest::newRow("size160") << QString(":/logfiles/size160.txt") << 160 << 80; - } - - - void test_SetLogEntries() - { - QFETCH(QString, fileName); - QFETCH(int, logEntriesSize); - QFETCH(int, count); - - QFile file(fileName); - file.open(QIODevice::ReadOnly); - QTextStream stream(&file); - QSignalSpy spy(mModel.data(), &LogModel::fireVisibleAreaChanged); - - mModel->setLogEntries(stream); - QCOMPARE(mModel->mLogEntries.size(), logEntriesSize); - QCOMPARE(mModel->mCount, count); - QCOMPARE(mModel->mIndex, 0); - QCOMPARE(spy.count(), 1); - } - - - void test_OnNewLogMsg_data() - { - QTest::addColumn("msg"); - QTest::addColumn("fileName"); - QTest::addColumn("autoFlick"); - QTest::addColumn("index"); - QTest::addColumn("selectedFile"); - QTest::addColumn("visibleAreaChangedCounter"); - QTest::addColumn("newLogMsgCounter"); - - QTest::newRow("emptyFile_MsgAdded") << QString(" : ") << QString(":/logfiles/empty.txt") << true << 1 << 0 << 1 << 0; - QTest::newRow("emptyFile_MsgNotAdded") << QString(" : ") << QString(":/logfiles/empty.txt") << false << 0 << 1 << 0 << 0; - QTest::newRow("emptyFile_MsgAdded_ViewChanged") << QString("test : input") << QString(":/logfiles/empty.txt") << true << 0 << 0 << 2 << 1; - - QTest::newRow("MsgAdded_Size78") << QString() << QString(":/logfiles/size78.txt") << false << 5 << 0 << 1 << 0; - QTest::newRow("MsgAdded_ViewChanged_Size78") << QString("test : input") << QString(":/logfiles/size78.txt") << true << 0 << 0 << 2 << 1; - QTest::newRow("MsgNotAdded_Size78") << QString("test : input") << QString(":/logfiles/size78.txt") << true << 5 << 2 << 0 << 0; - - QTest::newRow("MsgAdded_Size80") << QString() << QString(":/logfiles/size80.txt") << false << 5 << 0 << 1 << 0; - QTest::newRow("MsgNotAdded_Size80") << QString("test : input") << QString(":/logfiles/size80.txt") << false << 0 << 1 << 0 << 0; - QTest::newRow("MsgAdded_ViewChanged_Size80") << QString("test : input") << QString(":/logfiles/size80.txt") << true << 0 << 0 << 2 << 1; - - QTest::newRow("MsgAdded_Size82") << QString("test : input") << QString(":/logfiles/size82.txt") << true << 0 << 0 << 1 << 0; - QTest::newRow("MsgNotAdded_Size82") << QString(" : ") << QString(":/logfiles/size82.txt") << true << 0 << 3 << 0 << 0; - QTest::newRow("MsgAdded_ViewChanged_Size82") << QString("test : input") << QString(":/logfiles/size82.txt") << true << 2 << 0 << 2 << 1; - } - - - void test_OnNewLogMsg() - { - QFETCH(QString, msg); - QFETCH(QString, fileName); - QFETCH(bool, autoFlick); - QFETCH(int, index); - QFETCH(int, selectedFile); - QFETCH(int, visibleAreaChangedCounter); - QFETCH(int, newLogMsgCounter); - - QFile file(fileName); - file.open(QIODevice::ReadOnly); - QTextStream stream(&file); - mModel->setLogEntries(stream); - int oldSize = mModel->mLogEntries.size(); - mModel->mSelectedLogFile = selectedFile; - mModel->mIndex = index; - mModel->mAutoFlick = autoFlick; - QSignalSpy spyVisibleAreaChanged(mModel.data(), &LogModel::fireVisibleAreaChanged); - QSignalSpy spyNewLogMsg(mModel.data(), &LogModel::fireNewLogMsg); - - mModel->onNewLogMsg(msg); - QCOMPARE(spyVisibleAreaChanged.count(), visibleAreaChangedCounter); - QCOMPARE(spyNewLogMsg.count(), newLogMsgCounter); - - if (visibleAreaChangedCounter == 0) - { - QCOMPARE(mModel->mLogEntries.size(), oldSize); - } - else - { - QCOMPARE(mModel->mLogEntries.size(), oldSize + 2); - } - } - - -}; - -QTEST_GUILESS_MAIN(test_LogModel) -#include "test_LogModel.moc" diff --git a/test/qt/qml/test_NumberModel.cpp b/test/qt/qml/test_NumberModel.cpp deleted file mode 100644 index 1272e9d..0000000 --- a/test/qt/qml/test_NumberModel.cpp +++ /dev/null @@ -1,356 +0,0 @@ -/*! - * \brief Unit tests for \ref NumberModel - * - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "NumberModel.h" - -#include "context/ChangePinContext.h" -#include "context/RemoteServiceContext.h" - -#include "MockCardConnectionWorker.h" - -#include -#include - -using namespace governikus; - - -class test_NumberModel - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void test_ResetContext() - { - NumberModel model; - QSharedPointer context(new WorkflowContext()); - QSharedPointer pinContext(new ChangePinContext()); - - QSignalSpy spyCanChanged(&model, &NumberModel::fireCanChanged); - QSignalSpy spyPinChanged(&model, &NumberModel::firePinChanged); - QSignalSpy spyNewPinChanged(&model, &NumberModel::fireNewPinChanged); - QSignalSpy spyCanAllowed(&model, &NumberModel::fireCanAllowedModeChanged); - QSignalSpy spyPukChanged(&model, &NumberModel::firePukChanged); - QSignalSpy spyReaderNameChanged(&model, &NumberModel::fireReaderInfoChanged); - QSignalSpy spyLastPaceResultChanged(&model, &NumberModel::fireInputErrorChanged); - - model.resetContext(); - QCOMPARE(spyCanChanged.count(), 1); - QCOMPARE(spyPinChanged.count(), 1); - QCOMPARE(spyNewPinChanged.count(), 1); - QCOMPARE(spyCanAllowed.count(), 1); - QCOMPARE(spyPukChanged.count(), 1); - QCOMPARE(spyReaderNameChanged.count(), 1); - QCOMPARE(spyLastPaceResultChanged.count(), 1); - - model.resetContext(context); - QCOMPARE(model.mContext, context); - QCOMPARE(spyCanChanged.count(), 2); - QCOMPARE(spyPinChanged.count(), 2); - QCOMPARE(spyNewPinChanged.count(), 2); - QCOMPARE(spyCanAllowed.count(), 2); - QCOMPARE(spyPukChanged.count(), 2); - QCOMPARE(spyReaderNameChanged.count(), 2); - QCOMPARE(spyLastPaceResultChanged.count(), 2); - Q_EMIT context->fireCanChanged(); - QCOMPARE(spyCanChanged.count(), 3); - Q_EMIT context->firePinChanged(); - QCOMPARE(spyPinChanged.count(), 3); - Q_EMIT context->fireCanAllowedModeChanged(); - QCOMPARE(spyCanAllowed.count(), 3); - Q_EMIT context->fireReaderNameChanged(); - QCOMPARE(spyReaderNameChanged.count(), 3); - Q_EMIT context->fireLastPaceResultChanged(); - QCOMPARE(spyLastPaceResultChanged.count(), 3); - - model.resetContext(pinContext); - QCOMPARE(model.mContext, pinContext); - QCOMPARE(spyCanChanged.count(), 4); - QCOMPARE(spyPinChanged.count(), 4); - QCOMPARE(spyNewPinChanged.count(), 3); - QCOMPARE(spyCanAllowed.count(), 4); - QCOMPARE(spyPukChanged.count(), 3); - QCOMPARE(spyReaderNameChanged.count(), 4); - QCOMPARE(spyLastPaceResultChanged.count(), 4); - - Q_EMIT pinContext->fireCanChanged(); - QCOMPARE(spyCanChanged.count(), 5); - Q_EMIT pinContext->firePinChanged(); - QCOMPARE(spyPinChanged.count(), 5); - Q_EMIT pinContext->fireCanAllowedModeChanged(); - QCOMPARE(spyCanAllowed.count(), 5); - Q_EMIT pinContext->fireReaderNameChanged(); - QCOMPARE(spyReaderNameChanged.count(), 5); - Q_EMIT pinContext->fireLastPaceResultChanged(); - QCOMPARE(spyLastPaceResultChanged.count(), 5); - Q_EMIT pinContext->fireNewPinChanged(); - QCOMPARE(spyNewPinChanged.count(), 4); - Q_EMIT pinContext->firePukChanged(); - QCOMPARE(spyPukChanged.count(), 4); - } - - - void test_Can() - { - NumberModel model; - QSharedPointer context(new WorkflowContext()); - - const QString can = QStringLiteral("111111"); - model.setCan(can); - QCOMPARE(model.getCan(), QString()); - - model.mContext = context; - model.setCan(can); - QCOMPARE(model.getCan(), can); - QCOMPARE(context->getCan(), can); - } - - - void test_Pin() - { - NumberModel model; - QSharedPointer context(new WorkflowContext()); - - const QString pin = QStringLiteral("111111"); - model.setPin(pin); - QCOMPARE(model.getPin(), QString()); - - model.mContext = context; - model.setPin(pin); - QCOMPARE(model.getPin(), pin); - QCOMPARE(context->getPin(), pin); - } - - - void test_NewPin() - { - NumberModel model; - QSharedPointer workflowContext(new WorkflowContext()); - QSharedPointer changePinContext(new ChangePinContext()); - QSharedPointer remoteServiceContext(new RemoteServiceContext()); - const QString pin = QStringLiteral("111111"); - - model.mContext = workflowContext; - model.setNewPin(pin); - QCOMPARE(model.getNewPin(), QString()); - - model.mContext = changePinContext; - model.setNewPin(pin); - QCOMPARE(changePinContext->getNewPin(), pin); - QCOMPARE(model.getNewPin(), pin); - - model.mContext = remoteServiceContext; - model.setNewPin(pin); - QCOMPARE(remoteServiceContext->getNewPin(), pin); - QCOMPARE(model.getNewPin(), QString()); - } - - - void test_Puk() - { - NumberModel model; - QSharedPointer workflowContext(new WorkflowContext()); - const QString puk = QStringLiteral("111111"); - - model.mContext = workflowContext; - model.setPuk(puk); - QCOMPARE(workflowContext->getPuk(), puk); - QCOMPARE(model.getPuk(), puk); - } - - - void test_OnReaderInfoChanged() - { - NumberModel model; - QSharedPointer context(new WorkflowContext()); - QSignalSpy spyReaderNameChanged(&model, &NumberModel::fireReaderInfoChanged); - - const QString readerName = QStringLiteral("name"); - const QString test = QStringLiteral("test"); - - model.onReaderInfoChanged(readerName); - QCOMPARE(spyReaderNameChanged.count(), 0); - - context->setReaderName(test); - model.mContext = context; - model.onReaderInfoChanged(readerName); - QCOMPARE(spyReaderNameChanged.count(), 0); - - context->setReaderName(readerName); - model.mContext = context; - model.onReaderInfoChanged(readerName); - QCOMPARE(spyReaderNameChanged.count(), 1); - } - - - void test_CanAllowed() - { - NumberModel model; - QSharedPointer context(new WorkflowContext()); - - QVERIFY(!model.isCanAllowedMode()); - - model.mContext = context; - QVERIFY(!model.isCanAllowedMode()); - - context->setCanAllowedMode(true); - QVERIFY(model.isCanAllowedMode()); - } - - - void test_PinDeactivatedFalse() - { - NumberModel model; - QSharedPointer context(new WorkflowContext()); - - QVERIFY(!model.isPinDeactivated()); - - model.mContext = context; - QVERIFY(!model.isPinDeactivated()); - } - - - void test_Error() - { - QThread connectionThread; - connectionThread.start(); - - NumberModel model; - QSharedPointer context(new WorkflowContext()); - - QCOMPARE(model.getInputError(), QString()); - QVERIFY(!model.hasError()); - - context->setLastPaceResult(CardReturnCode::OK); - model.mContext = context; - QCOMPARE(model.getInputError(), QString()); - QVERIFY(!model.hasError()); - - context->setLastPaceResult(CardReturnCode::CANCELLATION_BY_USER); - QCOMPARE(model.getInputError(), QString()); - QVERIFY(!model.hasError()); - - QSharedPointer worker(new MockCardConnectionWorker()); - worker->moveToThread(&connectionThread); - QSharedPointer connection(new CardConnection(worker)); - context->setCardConnection(connection); - - context->setLastPaceResult(CardReturnCode::INVALID_PIN); - QCOMPARE(model.getInputError(), tr("The given PIN is not correct. You have 2 tries to enter the correct PIN.")); - QVERIFY(model.hasError()); - - context->setLastPaceResult(CardReturnCode::INVALID_PIN_2); - QCOMPARE(model.getInputError(), tr("You have entered the wrong PIN twice. " - "Prior to a third attempt, you have to enter your six-digit card access number first. " - "You can find your card access number on the front of your ID card.")); - QVERIFY(model.hasError()); - - context->setLastPaceResult(CardReturnCode::INVALID_PIN_3); - QCOMPARE(model.getInputError(), tr("You have entered a wrong PIN three times. " - "Your PIN is now blocked. " - "You have to enter the PUK now for unblocking.")); - QVERIFY(model.hasError()); - - context->setLastPaceResult(CardReturnCode::INVALID_CAN); - model.mContext = context; - QCOMPARE(model.getInputError(), tr("You have entered a wrong CAN, please try again.")); - - context->setLastPaceResult(CardReturnCode::INVALID_PUK); - QCOMPARE(model.getInputError(), tr("You have entered a wrong PUK. " - "Please try again.")); - QVERIFY(model.hasError()); - - context->setLastPaceResult(CardReturnCode::UNKNOWN); - QCOMPARE(model.getInputError(), tr("An unexpected error has occurred during processing.")); - - context->setLastPaceResult(CardReturnCode::UNEXPECTED_TRANSMIT_STATUS); - model.mContext = context; - QCOMPARE(model.getInputError(), QStringLiteral("%1 %3.").arg( - tr("A protocol error occurred. Please make sure that your ID card is placed correctly on the card reader and try again. If the problem occurs again, please contact our support at"), - tr("https://www.ausweisapp.bund.de/en/qa/support/"), - tr("AusweisApp2 Support"))); - QVERIFY(model.hasError()); - - connectionThread.quit(); - connectionThread.wait(); - } - - - void test_GetRetryCounter() - { - QThread connectionThread; - connectionThread.start(); - - NumberModel model; - QSharedPointer context(new WorkflowContext()); - - QCOMPARE(model.getRetryCounter(), -1); - - model.mContext = context; - QCOMPARE(model.getRetryCounter(), -1); - - const QString name = QStringLiteral("name"); - CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), - 3, true, false); - MockReader reader(name); - reader.getReaderInfo().setCardInfo(cardInfo); - QSharedPointer worker(new MockCardConnectionWorker(&reader)); - worker->moveToThread(&connectionThread); - QSharedPointer connection(new CardConnection(worker)); - context->setCardConnection(connection); - QCOMPARE(model.getRetryCounter(), 3); - - connectionThread.quit(); - connectionThread.wait(); - } - - - void test_RequestTransportPin_data() - { - QTest::addColumn >("context"); - QTest::addColumn("contextRequestedTransportPin"); - - QTest::newRow("WorkflowContext") << QSharedPointer(new WorkflowContext()) << false; - QTest::newRow("ChangePinContext") << QSharedPointer(new ChangePinContext()) << false; - QTest::newRow("ChangePinContext-false") << QSharedPointer(new ChangePinContext(false)) << false; - QTest::newRow("ChangePinContext-true") << QSharedPointer(new ChangePinContext(true)) << true; - } - - - void test_RequestTransportPin() - { - QFETCH(QSharedPointer, context); - QFETCH(bool, contextRequestedTransportPin); - - NumberModel model; - QVERIFY(!model.isRequestTransportPin()); - - model.setRequestTransportPin(true); - QVERIFY(model.isRequestTransportPin()); - model.setRequestTransportPin(false); - QVERIFY(!model.isRequestTransportPin()); - - model.resetContext(context); - QCOMPARE(model.isRequestTransportPin(), contextRequestedTransportPin); - - model.setRequestTransportPin(true); - model.resetContext(context); - QVERIFY(model.isRequestTransportPin()); - - model.setRequestTransportPin(false); - QVERIFY(!model.isRequestTransportPin()); - model.setRequestTransportPin(true); - QVERIFY(model.isRequestTransportPin()); - - model.resetContext(); - QVERIFY(!model.isRequestTransportPin()); - } - - -}; - -QTEST_GUILESS_MAIN(test_NumberModel) -#include "test_NumberModel.moc" diff --git a/test/qt/qml/test_ProviderCategoryFilterModel.cpp b/test/qt/qml/test_ProviderCategoryFilterModel.cpp deleted file mode 100644 index 86030c3..0000000 --- a/test/qt/qml/test_ProviderCategoryFilterModel.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/*! - * \brief Unit tests for \ref ProviderCategoryFilterModel - * - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "ProviderCategoryFilterModel.h" - -#include - - -using namespace governikus; - - -class test_ProviderCategoryFilterModel - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void test_UpdateSearchString() - { - ProviderCategoryFilterModel model; - QSignalSpy spy(&model, &ProviderCategoryFilterModel::fireCriteriaChanged); - const QString search = QStringLiteral("search"); - - model.updateSearchString(search); - QCOMPARE(model.getSearchString(), search); - QCOMPARE(spy.count(), 1); - } - - - void test_CategorySelection() - { - ProviderCategoryFilterModel model; - QSignalSpy spy(&model, &ProviderCategoryFilterModel::fireCriteriaChanged); - const QString category1 = QStringLiteral("CATEGORY"); - const QString category2 = QStringLiteral("testCATEGORY"); - - model.setCategorySelection(QString()); - QVERIFY(model.mSelectedCategories.isEmpty()); - QCOMPARE(spy.count(), 1); - - model.setCategorySelection(category1); - QVERIFY(model.mSelectedCategories.contains("category")); - QCOMPARE(spy.count(), 2); - - model.updateCategorySelection(category2, true); - QVERIFY(model.mSelectedCategories.contains("testcategory")); - QCOMPARE(spy.count(), 3); - - model.updateCategorySelection(category2, true); - QVERIFY(model.mSelectedCategories.contains("testcategory")); - QCOMPARE(spy.count(), 3); - - model.updateCategorySelection(category1, false); - QVERIFY(model.mSelectedCategories.contains("testcategory")); - QVERIFY(!model.mSelectedCategories.contains("category")); - QCOMPARE(spy.count(), 4); - } - - - void test_SortByCategoryFirst() - { - ProviderCategoryFilterModel model; - - model.sortByCategoryFirst(true); - QCOMPARE(model.sortRole(), ProviderModel::SORT_ROLE); - - model.sortByCategoryFirst(false); - QCOMPARE(model.sortRole(), ProviderModel::SHORTNAME); - } - - -}; - -QTEST_GUILESS_MAIN(test_ProviderCategoryFilterModel) -#include "test_ProviderCategoryFilterModel.moc" diff --git a/test/qt/qml/test_ProviderModel.cpp b/test/qt/qml/test_ProviderModel.cpp deleted file mode 100644 index 48a2f4c..0000000 --- a/test/qt/qml/test_ProviderModel.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/*! - * \brief Unit tests for \ref ProviderModel - * - * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "ProviderModel.h" - -#include -#include - - -using namespace governikus; - - -class test_ProviderModel - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void createAmountStringForCents() - { - QVERIFY(ProviderModel::createAmountString(3.9).contains(QString("3.9"))); - } - - - void createAmountStringForEur() - { - QVERIFY(ProviderModel::createAmountString(289).contains(QString("2.89"))); - } - - - void createCostStringMinute() - { - const auto& msg = ProviderModel::createCostString(1.9, 2.9); - QVERIFY(msg.contains(QString("1.9"))); - QVERIFY(!msg.contains(QString("2.9"))); - } - - - void createCostStringCall() - { - const auto& msg = ProviderModel::createCostString(0.0, 2.9); - QVERIFY(!msg.contains(QString("0.0"))); - QVERIFY(msg.contains(QString("2.9"))); - } - - - void createCostStringEmpty() - { - const auto& msg = ProviderModel::createCostString(0.0, 0.0); - QVERIFY(msg.isEmpty()); - } - - - void createCostStringNullCost() - { - const auto& msg = ProviderModel::createCostString(CallCost()); - QVERIFY(msg.isNull()); - } - - - void createCostString() - { - const auto& msg = ProviderModel::createCostString(CallCost(0.0, 3.9, 0.0, 42.0, 0.0)); - QVERIFY(!msg.isNull()); - } - - -}; - -QTEST_GUILESS_MAIN(test_ProviderModel) -#include "test_ProviderModel.moc" diff --git a/test/qt/qml/test_RemoteServiceModel.cpp b/test/qt/qml/test_RemoteServiceModel.cpp deleted file mode 100644 index c151014..0000000 --- a/test/qt/qml/test_RemoteServiceModel.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/*! - * \brief Unit tests for \ref ProviderModel - * - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "RemoteServiceModel.h" - -#include "context/RemoteServiceContext.h" -#include "MockRemoteServer.h" - -#include -#include - - -using namespace governikus; - -class test_RemoteServiceModel - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void initTestCase() - { - Env::setCreator(std::function([&] { - return new MockRemoteServer(); - })); - } - - - void test_ReaderPlugInType() - { - const QSharedPointer context(new RemoteServiceContext()); - RemoteServiceModel model; - const ReaderManagerPlugInType input1 = ReaderManagerPlugInType::NFC; - const ReaderManagerPlugInType input2 = ReaderManagerPlugInType::UNKNOWN; - - model.setReaderPlugInType(input1); - QCOMPARE(model.getReaderPlugInType(), ReaderManagerPlugInType::UNKNOWN); - - model.resetContext(context); - model.setReaderPlugInType(input1); - QCOMPARE(model.getReaderPlugInType(), input1); - - model.setReaderPlugInType(input2); - QCOMPARE(model.getReaderPlugInType(), ReaderManagerPlugInType::UNKNOWN); - } - - - void test_ErrorMessage() - { - RemoteServiceModel model; - - QCOMPARE(model.getErrorMessage(true, true, true), QString()); - QCOMPARE(model.getErrorMessage(false, true, true), tr("NFC is not available on your device.")); - QCOMPARE(model.getErrorMessage(true, false, true), tr("Please enable NFC to use the remote service.")); - QCOMPARE(model.getErrorMessage(true, true, false), tr("Please connect your WiFi to use the remote service.")); - } - - - void test_CancelPasswordRequest() - { - const QSharedPointer context(new RemoteServiceContext()); - RemoteServiceModel model; - QSignalSpy spy(context.data(), &RemoteServiceContext::fireCancelPasswordRequest); - - model.cancelPasswordRequest(); - QCOMPARE(spy.count(), 0); - - model.mContext = context; - model.cancelPasswordRequest(); - QCOMPARE(spy.count(), 1); - } - - - void test_Running() - { - const QSharedPointer context(new RemoteServiceContext()); - RemoteServiceModel model; - QSignalSpy spyStartWorkflow(&model, &RemoteServiceModel::fireStartWorkflow); - QSignalSpy spyCancelWorkflow(context.data(), &RemoteServiceContext::fireCancelWorkflow); - QSignalSpy spyIsRunningChanged(&model, &RemoteServiceModel::fireIsRunningChanged); - - QVERIFY(!model.isRunning()); - - model.mContext = context; - QVERIFY(!model.isRunning()); - - context->getRemoteServer()->start(QString()); - QVERIFY(model.isRunning()); - model.setRunning(false); - QCOMPARE(spyIsRunningChanged.count(), 1); - QCOMPARE(spyCancelWorkflow.count(), 1); - - context->getRemoteServer()->stop(); - model.setRunning(true); - QCOMPARE(spyIsRunningChanged.count(), 2); - QCOMPARE(spyStartWorkflow.count(), 1); - } - - - void test_ResetContext() - { - const QSharedPointer context(new RemoteServiceContext()); - RemoteServiceModel model; - - QSignalSpy spyConnectedChanged(&model, &RemoteServiceModel::fireConnectedChanged); - QSignalSpy spyCurrentStateChanged(&model, &WorkflowModel::fireCurrentStateChanged); - QSignalSpy spyIsRunningChanged(&model, &RemoteServiceModel::fireIsRunningChanged); - QSignalSpy spyPskChanged(&model, &RemoteServiceModel::firePskChanged); - QSignalSpy spyConnectedClientDeviceNameChanged(&model, &RemoteServiceModel::fireConnectedClientDeviceNameChanged); - - model.resetContext(context); - QCOMPARE(model.mContext, context); - QCOMPARE(spyCurrentStateChanged.count(), 1); - QCOMPARE(spyConnectedChanged.count(), 1); - - Q_EMIT context->fireStateChanged(QString()); - QCOMPARE(spyIsRunningChanged.count(), 1); - - Q_EMIT context->getRemoteServer()->firePskChanged(QByteArray()); - QCOMPARE(spyPskChanged.count(), 1); - - Q_EMIT context->getRemoteServer()->fireConnectedChanged(true); - QCOMPARE(spyConnectedChanged.count(), 2); - QCOMPARE(spyConnectedClientDeviceNameChanged.count(), 1); - } - - - void test_SetPairing() - { - const QSharedPointer context(new RemoteServiceContext()); - RemoteServiceModel model; - model.mContext = context; - const QSharedPointer server = qSharedPointerCast(context->getRemoteServer()); - - model.setPairing(true); - QVERIFY(server->getPairing()); - - model.setPairing(false); - QVERIFY(!server->getPairing()); - } - - -}; - -QTEST_GUILESS_MAIN(test_RemoteServiceModel) -#include "test_RemoteServiceModel.moc" diff --git a/test/qt/qml/test_SelfAuthModel.cpp b/test/qt/qml/test_SelfAuthModel.cpp deleted file mode 100644 index 7d47da3..0000000 --- a/test/qt/qml/test_SelfAuthModel.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/*! - * \brief Unit tests for \ref SelfAuthModel - * - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "SelfAuthModel.h" - -#include "context/SelfAuthContext.h" - -#include "MockCardConnectionWorker.h" - -#include - - -using namespace governikus; - -class test_SelfAuthModel - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void test_ResetContext() - { - SelfAuthModel model; - const QSharedPointer context(new SelfAuthContext()); - - model.resetContext(nullptr); - QVERIFY(!model.mContext); - - model.resetContext(context); - QCOMPARE(model.mContext, context); - } - - - void test_StartWorkflow() - { - SelfAuthModel model; - QSignalSpy spy(&model, &SelfAuthModel::fireStartWorkflow); - - model.startWorkflow(); - QCOMPARE(spy.count(), 1); - } - - - void test_CancelWorkflow() - { - SelfAuthModel model; - const QSharedPointer context(new SelfAuthContext()); - QSignalSpy spy(context.data(), &SelfAuthContext::fireCancelWorkflow); - - model.cancelWorkflow(); - QCOMPARE(spy.count(), 0); - - model.mContext = context; - model.cancelWorkflow(); - QCOMPARE(spy.count(), 1); - } - - - void test_IsBasicReader() - { - QThread workerThread; - workerThread.start(); - - SelfAuthModel model; - - QVERIFY(model.isBasicReader()); - - const QSharedPointer worker(new MockCardConnectionWorker()); - worker->moveToThread(&workerThread); - const QSharedPointer context(new SelfAuthContext()); - const QSharedPointer connection(new CardConnection(worker)); - context->setCardConnection(connection); - ReaderInfo info; - - info.setBasicReader(true); - connection->mReaderInfo = info; - model.mContext = context; - QVERIFY(model.isBasicReader()); - - info.setBasicReader(false); - connection->mReaderInfo = info; - QVERIFY(!model.isBasicReader()); - - workerThread.quit(); - workerThread.wait(); - } - - - void test_RoleNames() - { - SelfAuthModel model; - - QVERIFY(model.roleNames().keys().contains(SelfAuthModel::DataRoles::NAME)); - QVERIFY(model.roleNames().keys().contains(SelfAuthModel::DataRoles::VALUE)); - QVERIFY(model.roleNames().values().contains("name")); - QVERIFY(model.roleNames().values().contains("value")); - } - - -}; - -QTEST_GUILESS_MAIN(test_SelfAuthModel) -#include "test_SelfAuthModel.moc" diff --git a/test/qt/remote_device/test_RemoteClientImpl.cpp b/test/qt/remote_device/test_RemoteClientImpl.cpp index c2d4445..491225c 100644 --- a/test/qt/remote_device/test_RemoteClientImpl.cpp +++ b/test/qt/remote_device/test_RemoteClientImpl.cpp @@ -11,7 +11,7 @@ #include "messages/IfdEstablishContext.h" #include -#include +#include using namespace governikus; diff --git a/test/qt/remote_device/test_RemoteConnector.cpp b/test/qt/remote_device/test_RemoteConnector.cpp index 66642b2..809272c 100644 --- a/test/qt/remote_device/test_RemoteConnector.cpp +++ b/test/qt/remote_device/test_RemoteConnector.cpp @@ -13,7 +13,7 @@ #include "RemoteWebSocketServer.h" #include "SecureStorage.h" -#include +#include #include #include @@ -104,7 +104,7 @@ class test_RemoteConnector const QVariant errorCodeVariant = arguments.at(1); QVERIFY(errorCodeVariant.canConvert()); - const RemoteErrorCode errorCode = errorCodeVariant.value(); + const auto errorCode = errorCodeVariant.value(); const QUrl remoteUrl = descr.getUrl(); const QString remoteAddress = remoteUrl.host(); @@ -274,13 +274,13 @@ class test_RemoteConnector const KeyPair pair = KeyPair::generate(); QVERIFY(pair.isValid()); - QSslConfiguration config = SecureStorage::getInstance().getTlsConfigRemote().getConfiguration(); + QSslConfiguration config = Env::getSingleton()->getTlsConfigRemote().getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); config.setCaCertificates({KeyPair::generate().getCertificate(), settings.getCertificate(), KeyPair::generate().getCertificate()}); QTest::newRow("paired") << QString() << config << QList({KeyPair::generate().getCertificate(), pair.getCertificate(), KeyPair::generate().getCertificate()}); - config = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); + config = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); QTest::newRow("unpaired") << QString("123456") << config << QList(); @@ -363,7 +363,7 @@ class test_RemoteConnector QSignalSpy spySocketError(&webSocketServer, &QWebSocketServer::serverError); QSignalSpy spySocketSuccess(&webSocketServer, &QWebSocketServer::newConnection); - QSslConfiguration config = SecureStorage::getInstance().getTlsConfigRemote().getConfiguration(); + QSslConfiguration config = Env::getSingleton()->getTlsConfigRemote().getConfiguration(); const KeyPair pair = KeyPair::generate(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); diff --git a/test/qt/remote_device/test_RemoteDeviceDescriptor.cpp b/test/qt/remote_device/test_RemoteDeviceDescriptor.cpp index 56e730a..51ce937 100644 --- a/test/qt/remote_device/test_RemoteDeviceDescriptor.cpp +++ b/test/qt/remote_device/test_RemoteDeviceDescriptor.cpp @@ -6,7 +6,7 @@ #include "messages/Discovery.h" -#include +#include using namespace governikus; diff --git a/test/qt/remote_device/test_RemoteDeviceListImpl.cpp b/test/qt/remote_device/test_RemoteDeviceListImpl.cpp index f07af51..e01fe71 100644 --- a/test/qt/remote_device/test_RemoteDeviceListImpl.cpp +++ b/test/qt/remote_device/test_RemoteDeviceListImpl.cpp @@ -6,7 +6,7 @@ #include "messages/Discovery.h" -#include +#include using namespace governikus; diff --git a/test/qt/remote_device/test_RemoteDeviceModel.cpp b/test/qt/remote_device/test_RemoteDeviceModel.cpp index 1e7b885..3292f24 100644 --- a/test/qt/remote_device/test_RemoteDeviceModel.cpp +++ b/test/qt/remote_device/test_RemoteDeviceModel.cpp @@ -6,56 +6,65 @@ #include - using namespace governikus; +Q_DECLARE_METATYPE(RemoteDeviceModel::SettingsRemoteRoles) class test_RemoteDeviceModel : public QObject { Q_OBJECT + QSharedPointer mModel; + QSharedPointer mEntry; + QString mName; private Q_SLOTS: + void init() + { + mName = QStringLiteral("name"); + mModel.reset(new RemoteDeviceModel()); + mEntry.reset(new RemoteDeviceModelEntry(mName)); + } + + + void cleanup() + { + mModel.clear(); + mEntry.clear(); + } + + void test_Paired() { - const QString name = QStringLiteral("name"); - RemoteDeviceModelEntry entry(name); - QVERIFY(!entry.isPaired()); - entry.setPaired(true); - QVERIFY(entry.isPaired()); + QVERIFY(!mEntry->isPaired()); + mEntry->setPaired(true); + QVERIFY(mEntry->isPaired()); } void test_DeviceName() { - const QString name = QStringLiteral("name"); - RemoteDeviceModelEntry entry(name); - QCOMPARE(entry.getDeviceName(), name); + QCOMPARE(mEntry->getDeviceName(), mName); } void test_Id() { - const QString name = QStringLiteral("name"); const QString id = QStringLiteral("id"); - RemoteDeviceModelEntry entry(name); - QCOMPARE(entry.getId(), QString()); + QCOMPARE(mEntry->getId(), QString()); - entry.setId(id); - QCOMPARE(entry.getId(), id); + mEntry->setId(id); + QCOMPARE(mEntry->getId(), id); } void test_NetworkVisible() { - const QString name = QStringLiteral("name"); - RemoteDeviceModelEntry entry(name); + QVERIFY(!mEntry->isNetworkVisible()); - QVERIFY(!entry.isNetworkVisible()); - - entry.setNetworkVisible(true); - QVERIFY(entry.isNetworkVisible()); + mEntry->setNetworkVisible(true); + QVERIFY(mEntry->isNetworkVisible()); } @@ -65,7 +74,9 @@ class test_RemoteDeviceModel const QString id = QStringLiteral("id"); const QDateTime time(QDateTime::currentDateTime()); RemoteDeviceModelEntry entry1(name); - RemoteDeviceModelEntry entry2(name, id, true, true, true, time); + const RemoteDeviceDescriptor descriptor = RemoteDeviceDescriptor(); + QSharedPointer pointer(new RemoteDeviceListEntry(descriptor)); + RemoteDeviceModelEntry entry2(name, id, true, true, true, time, pointer); QVERIFY(!entry1.isSupported()); QVERIFY(entry2.isSupported()); @@ -89,31 +100,28 @@ class test_RemoteDeviceModel void test_LastConnected() { - const QString name = QStringLiteral("name"); - RemoteDeviceModelEntry entry(name); QDateTime time(QDateTime::currentDateTime()); - QCOMPARE(entry.getLastConnected(), QDateTime()); + QCOMPARE(mEntry->getLastConnected(), QDateTime()); - entry.setLastConnected(time); - QCOMPARE(entry.getLastConnected(), time); + mEntry->setLastConnected(time); + QCOMPARE(mEntry->getLastConnected(), time); } void test_RoleNames() { - RemoteDeviceModel model; - QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::REMOTE_DEVICE_NAME)); - QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::LAST_CONNECTED)); - QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::DEVICE_ID)); - QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::IS_NETWORK_VISIBLE)); - QVERIFY(model.roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::IS_SUPPORTED)); + QVERIFY(mModel->roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::REMOTE_DEVICE_NAME)); + QVERIFY(mModel->roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::LAST_CONNECTED)); + QVERIFY(mModel->roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::DEVICE_ID)); + QVERIFY(mModel->roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::IS_NETWORK_VISIBLE)); + QVERIFY(mModel->roleNames().keys().contains(RemoteDeviceModel::SettingsRemoteRoles::IS_SUPPORTED)); - QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("remoteDeviceName"))); - QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("lastConnected"))); - QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("deviceId"))); - QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("isNetworkVisible"))); - QVERIFY(model.roleNames().values().contains(QByteArrayLiteral("isSupported"))); + QVERIFY(mModel->roleNames().values().contains(QByteArrayLiteral("remoteDeviceName"))); + QVERIFY(mModel->roleNames().values().contains(QByteArrayLiteral("lastConnected"))); + QVERIFY(mModel->roleNames().values().contains(QByteArrayLiteral("deviceId"))); + QVERIFY(mModel->roleNames().values().contains(QByteArrayLiteral("isNetworkVisible"))); + QVERIFY(mModel->roleNames().values().contains(QByteArrayLiteral("isSupported"))); } @@ -121,59 +129,104 @@ class test_RemoteDeviceModel { RemoteDeviceModelEntry entry; - RemoteDeviceModel model; + QCOMPARE(mModel->getStatus(entry), QString("Not connected")); - QCOMPARE(model.getStatus(entry), QString("Not connected")); + mModel->mAllRemoteReaders.insert(0, entry); - model.mAllRemoteReaders.insert(0, entry); - - QCOMPARE(model.getStatus(entry), QString("Unsupported version")); + QCOMPARE(mModel->getStatus(entry), QString("Unsupported")); entry.mSupported = true; - QCOMPARE(model.getStatus(entry), QString("Not paired")); + QCOMPARE(mModel->getStatus(entry), QString("Not paired")); entry.setPaired(true); - QCOMPARE(model.getStatus(entry), QString("Paired, but unavailable")); + QCOMPARE(mModel->getStatus(entry), QString("Paired, but unavailable")); entry.setNetworkVisible(true); - QCOMPARE(model.getStatus(entry), QString("Paired and available")); + QCOMPARE(mModel->getStatus(entry), QString("Available")); entry.mSupported = false; - QCOMPARE(model.getStatus(entry), QString("Paired, but unsupported")); + QCOMPARE(mModel->getStatus(entry), QString("Paired, but unsupported")); } void test_HeaderData() { - RemoteDeviceModel model; - - QCOMPARE(model.headerData(3, Qt::Vertical, 3), QVariant()); - QCOMPARE(model.headerData(0, Qt::Horizontal, 3), QVariant()); - QCOMPARE(model.headerData(0, Qt::Horizontal, 0), QVariant("Device")); - QCOMPARE(model.headerData(1, Qt::Horizontal, 0), QVariant("Status")); + QCOMPARE(mModel->headerData(3, Qt::Vertical, 3), QVariant()); + QCOMPARE(mModel->headerData(0, Qt::Horizontal, 3), QVariant()); + QCOMPARE(mModel->headerData(0, Qt::Horizontal, 0), QVariant("Device")); + QCOMPARE(mModel->headerData(1, Qt::Horizontal, 0), QVariant("Status")); } void test_ColumnCount() { - RemoteDeviceModel model; - QCOMPARE(model.columnCount(), 2); + QCOMPARE(mModel->columnCount(), 2); } - void test_GetRemoteListEntry() + void test_GetRemoteDeviceListEntryId() { - RemoteDeviceModel model; RemoteDeviceModelEntry entry1; RemoteDeviceModelEntry entry2; entry1.setId(QString("id")); - QCOMPARE(model.getRemoteDeviceListEntry(QString("id")), QSharedPointer()); + QCOMPARE(mModel->getRemoteDeviceListEntry(QString("id")), QSharedPointer()); - model.mAllRemoteReaders.insert(0, entry1); - model.mAllRemoteReaders.insert(1, entry2); + mModel->mAllRemoteReaders.insert(0, entry1); + mModel->mAllRemoteReaders.insert(1, entry2); - QCOMPARE(model.getRemoteDeviceListEntry(QString("id")), nullptr); + QCOMPARE(mModel->getRemoteDeviceListEntry(QString("id")), nullptr); + } + + + void test_GetRemoteDeviceListEntryModelIndex() + { + QSharedPointer listEntry(new RemoteDeviceListEntry(RemoteDeviceDescriptor())); + RemoteDeviceModelEntry entry1(QString("entry 1"), QString("01"), listEntry); + RemoteDeviceModelEntry entry2(QString("entry 2")); + mModel->mAllRemoteReaders << entry1 << entry2; + + const auto& index1 = mModel->createIndex(0, 0); + QCOMPARE(mModel->getRemoteDeviceListEntry(index1), listEntry); + + const auto& index2 = mModel->createIndex(1, 0); + QCOMPARE(mModel->getRemoteDeviceListEntry(index2), nullptr); + } + + + void test_Data_data() + { + QTest::addColumn("role"); + QTest::addColumn("row"); + QTest::addColumn("column"); + QTest::addColumn("output"); + + QTest::newRow("device name") << RemoteDeviceModel::SettingsRemoteRoles::REMOTE_DEVICE_NAME << 0 << 0 << QVariant(QString("reader 1")); + QTest::newRow("device status") << RemoteDeviceModel::SettingsRemoteRoles::REMOTE_DEVICE_STATUS << 1 << 0 << QVariant(QString("Unsupported")); + QTest::newRow("last connected") << RemoteDeviceModel::SettingsRemoteRoles::LAST_CONNECTED << 0 << 0 << QVariant(QString("14.05.2019 12:00 AM")); + QTest::newRow("device id") << RemoteDeviceModel::SettingsRemoteRoles::DEVICE_ID << 0 << 0 << QVariant(QString("test id")); + QTest::newRow("network visible") << RemoteDeviceModel::SettingsRemoteRoles::IS_NETWORK_VISIBLE << 1 << 0 << QVariant(bool(false)); + QTest::newRow("supported") << RemoteDeviceModel::SettingsRemoteRoles::IS_SUPPORTED << 0 << 0 << QVariant(bool(true)); + QTest::newRow("paired") << RemoteDeviceModel::SettingsRemoteRoles::IS_PAIRED << 0 << 0 << QVariant(bool(true)); + QTest::newRow("link quality") << RemoteDeviceModel::SettingsRemoteRoles::LINK_QUALITY << 0 << 0 << QVariant(int(0)); + } + + + void test_Data() + { + QFETCH(RemoteDeviceModel::SettingsRemoteRoles, role); + QFETCH(int, row); + QFETCH(int, column); + QFETCH(QVariant, output); + + QVector readers; + QSharedPointer listEntry(new RemoteDeviceListEntry(RemoteDeviceDescriptor())); + const RemoteDeviceModelEntry entry1(QString("reader 1"), QString("test id"), true, true, true, QDateTime(QDate(2019, 5, 14), QTime(0, 0)), listEntry); + const RemoteDeviceModelEntry entry2(QString("reader 2")); + readers << entry1 << entry2; + mModel->mAllRemoteReaders = readers; + const auto& index = mModel->createIndex(row, column); + QCOMPARE(mModel->data(index, role), output); } diff --git a/test/qt/remote_device/test_RemoteDisp.cpp b/test/qt/remote_device/test_RemoteDisp.cpp index 3d7d179..8a5b72b 100644 --- a/test/qt/remote_device/test_RemoteDisp.cpp +++ b/test/qt/remote_device/test_RemoteDisp.cpp @@ -14,8 +14,8 @@ #include "messages/IfdTransmit.h" #include "MockDataChannel.h" -#include -#include +#include +#include using namespace governikus; @@ -53,7 +53,7 @@ class RemoteDispatcherSpy RemoteDispatcherSpy::RemoteDispatcherSpy(const QSharedPointer pRemoteDispatcher) : mRemoteDispatcher(pRemoteDispatcher) , mClosed(false) - , mCloseCode(GlobalStatus::Code::RemoteReader_CloseCode_Undefined) + , mCloseCode(GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose) { const auto client = mRemoteDispatcher.objectCast(); if (client) @@ -154,7 +154,7 @@ class test_RemoteDisp channel->close(); QVERIFY(spy.isClosed()); - QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); + QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::No_Error); const QVector& senders = spy.getReceivedSignalSenders(); QCOMPARE(senders.size(), 1); @@ -170,7 +170,7 @@ class test_RemoteDisp channel->close(); QVERIFY(spy.isClosed()); - QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); + QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::No_Error); const QVector& senders = spy.getReceivedSignalSenders(); QCOMPARE(senders.size(), 1); @@ -273,7 +273,7 @@ class test_RemoteDisp // Destroying a remote dispatcher should close the underlying channel. clientDispatcher.reset(); QVERIFY(spy.isClosed()); - QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::RemoteReader_CloseCode_NormalClose); + QCOMPARE(spy.getCloseCode(), GlobalStatus::Code::No_Error); const QVector& senders = spy.getReceivedSignalSenders(); QCOMPARE(senders.size(), 1); diff --git a/test/qt/remote_device/test_RemoteReaderManagerPlugin.cpp b/test/qt/remote_device/test_RemoteReaderManagerPlugin.cpp index 88c0775..a5ca189 100644 --- a/test/qt/remote_device/test_RemoteReaderManagerPlugin.cpp +++ b/test/qt/remote_device/test_RemoteReaderManagerPlugin.cpp @@ -6,6 +6,7 @@ #include "plugin/RemoteReaderManagerPlugIn.h" +#include "AppSettings.h" #include "Env.h" #include "messages/IfdConnect.h" #include "messages/IfdConnect.h" @@ -23,8 +24,8 @@ #include #include -#include -#include +#include +#include using namespace governikus; @@ -67,8 +68,8 @@ bool MockRemoteClient::isDetecting() void MockRemoteClient::establishConnection(const QSharedPointer& pEntry, const QString& pPsk) { - Q_UNUSED(pEntry); - Q_UNUSED(pPsk); + Q_UNUSED(pEntry) + Q_UNUSED(pPsk) } @@ -162,7 +163,12 @@ class test_RemoteReaderManagerPlugIn QSignalSpy spyAdded(mPlugin.data(), &ReaderManagerPlugIn::fireReaderAdded); QSignalSpy spyRemoved(mPlugin.data(), &ReaderManagerPlugIn::fireReaderRemoved); - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true)); + ReaderInfo info(QStringLiteral("NFC Reader")); + info.setConnected(true); + info.setBasicReader(true); + info.setMaxApduLength(500); + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(false); + message.reset(new IfdStatus(info)); mDispatcher1->onReceived(message); QCOMPARE(mPlugin->getReaders().size(), 1); QCOMPARE(spySend.size(), 0); @@ -173,7 +179,8 @@ class test_RemoteReaderManagerPlugIn QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().isBasicReader(), true); QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 500); - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, false)); + info.setConnected(false); + message.reset(new IfdStatus(info)); mDispatcher1->onReceived(message); QCOMPARE(mPlugin->getReaders().size(), 0); QCOMPARE(spySend.size(), 0); @@ -181,7 +188,8 @@ class test_RemoteReaderManagerPlugIn QCOMPARE(spyRemoved.size(), 1); QCOMPARE(spyRemoved.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true)); + info.setConnected(true); + message.reset(new IfdStatus(info)); mDispatcher1->onReceived(message); QCOMPARE(mPlugin->getReaders().size(), 1); QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader#TestContext")); @@ -208,7 +216,12 @@ class test_RemoteReaderManagerPlugIn QSignalSpy spyAdded(mPlugin.data(), &ReaderManagerPlugIn::fireReaderAdded); QSignalSpy spyRemoved(mPlugin.data(), &ReaderManagerPlugIn::fireReaderRemoved); - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(true), 500, true)); + ReaderInfo info(QStringLiteral("NFC Reader")); + info.setConnected(true); + info.setBasicReader(true); + info.setMaxApduLength(500); + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(true); + message.reset(new IfdStatus(info)); mDispatcher1->onReceived(message); QCOMPARE(spySend.size(), 0); QCOMPARE(spyAdded.size(), 1); @@ -236,10 +249,21 @@ class test_RemoteReaderManagerPlugIn QSignalSpy spyAdded(mPlugin.data(), &ReaderManagerPlugIn::fireReaderAdded); QSignalSpy spyRemoved(mPlugin.data(), &ReaderManagerPlugIn::fireReaderRemoved); - message.reset(new IfdStatus(QStringLiteral("NFC Reader 1"), PaceCapabilities(), 500, true)); + ReaderInfo info1(QStringLiteral("NFC Reader 1")); + info1.setMaxApduLength(500); + info1.setConnected(true); + info1.setBasicReader(true); + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(false); + message.reset(new IfdStatus(info1)); mDispatcher1->onReceived(message); - message.reset(new IfdStatus(QStringLiteral("NFC Reader 2"), PaceCapabilities(), 500, true)); + + ReaderInfo info2(QStringLiteral("NFC Reader 2")); + info2.setMaxApduLength(500); + info2.setConnected(true); + info2.setBasicReader(true); + message.reset(new IfdStatus(info2)); mDispatcher2->onReceived(message); + QCOMPARE(spySend1.size(), 0); QCOMPARE(spySend2.size(), 0); QCOMPARE(spyAdded.size(), 2); @@ -250,8 +274,10 @@ class test_RemoteReaderManagerPlugIn QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 1#TestContext")); QCOMPARE(mPlugin->getReaders().at(1)->getName(), QStringLiteral("NFC Reader 2#TestContext")); - message.reset(new IfdStatus(QStringLiteral("NFC Reader 1"), PaceCapabilities(), 500, false)); + info1.setConnected(false); + message.reset(new IfdStatus(info1)); mDispatcher1->onReceived(message); + QCOMPARE(spySend1.size(), 0); QCOMPARE(spySend2.size(), 0); QCOMPARE(spyAdded.size(), 0); @@ -260,8 +286,10 @@ class test_RemoteReaderManagerPlugIn QCOMPARE(mPlugin->getReaders().size(), 1); QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 2#TestContext")); - message.reset(new IfdStatus(QStringLiteral("NFC Reader 1"), PaceCapabilities(), 500, true)); + info1.setConnected(true); + message.reset(new IfdStatus(info1)); mDispatcher1->onReceived(message); + QCOMPARE(spySend1.size(), 0); QCOMPARE(spySend2.size(), 0); QCOMPARE(spyAdded.size(), 1); @@ -304,7 +332,13 @@ class test_RemoteReaderManagerPlugIn QCOMPARE(result->getType(), RemoteCardMessageType::IFDConnect); QSharedPointer message; - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true, false)); + + ReaderInfo info1(QStringLiteral("NFC Reader")); + info1.setMaxApduLength(500); + info1.setConnected(true); + message.reset(new IfdStatus(info1)); + info1.setBasicReader(true); + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(false); mDispatcher1->onReceived(message); QCOMPARE(mPlugin->getReaders().at(0)->getCard(), nullptr); @@ -313,7 +347,8 @@ class test_RemoteReaderManagerPlugIn QSignalSpy spyChanged(mPlugin->getReaders().at(0), &Reader::fireCardRetryCounterChanged); QSignalSpy spyUpdated(mPlugin->getReaders().at(0), &Reader::fireReaderPropertiesUpdated); - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 500, true, true)); + info1.setCardInfo(CardInfo(CardType::EID_CARD)); + message.reset(new IfdStatus(info1)); mDispatcher1->onReceived(message); QTRY_COMPARE(spySend.count(), 1); @@ -328,7 +363,8 @@ class test_RemoteReaderManagerPlugIn QCOMPARE(spyChanged.size(), 0); QCOMPARE(spyUpdated.size(), 0); - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, true)); + info1.setMaxApduLength(1); + message.reset(new IfdStatus(info1)); mDispatcher1->onReceived(message); QVERIFY(mPlugin->getReaders().at(0)->getCard() != nullptr); QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 1); @@ -339,7 +375,7 @@ class test_RemoteReaderManagerPlugIn QCOMPARE(spyUpdated.size(), 1); QCOMPARE(spyUpdated.takeFirst().at(0).toString(), QStringLiteral("NFC Reader#TestContext")); - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, true)); + message.reset(new IfdStatus(info1)); mDispatcher1->onReceived(message); QVERIFY(mPlugin->getReaders().at(0)->getCard() != nullptr); QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 1); @@ -349,7 +385,11 @@ class test_RemoteReaderManagerPlugIn QCOMPARE(spyChanged.size(), 0); QCOMPARE(spyUpdated.size(), 0); - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, false)); + ReaderInfo info2(QStringLiteral("NFC Reader")); + info2.setMaxApduLength(1); + info2.setConnected(true); + info2.setBasicReader(true); + message.reset(new IfdStatus(info2)); mDispatcher1->onReceived(message); QCOMPARE(mPlugin->getReaders().at(0)->getCard(), nullptr); QVERIFY(!mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); @@ -359,7 +399,8 @@ class test_RemoteReaderManagerPlugIn QCOMPARE(spyChanged.size(), 0); QCOMPARE(spyUpdated.size(), 0); - message.reset(new IfdStatus(QStringLiteral("NFC Reader"), PaceCapabilities(), 1, true, true)); + info1.setCardInfo(CardInfo(CardType::EID_CARD)); + message.reset(new IfdStatus(info1)); mDispatcher1->onReceived(message); QTRY_COMPARE(spySend.count(), 1); result = qvariant_cast >(spySend.takeFirst().at(0)); diff --git a/test/qt/remote_device/test_RemoteTlsServer.cpp b/test/qt/remote_device/test_RemoteTlsServer.cpp index 083f0a9..89789fe 100644 --- a/test/qt/remote_device/test_RemoteTlsServer.cpp +++ b/test/qt/remote_device/test_RemoteTlsServer.cpp @@ -49,7 +49,7 @@ class test_RemoteTlsServer server.setPairing(serverPairing); server.listen(); - auto config = SecureStorage::getInstance().getTlsConfigRemote(clientConfig).getConfiguration(); + auto config = Env::getSingleton()->getTlsConfigRemote(clientConfig).getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); @@ -79,7 +79,7 @@ class test_RemoteTlsServer server.setPairing(); server.listen(); - auto config = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); + auto config = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); @@ -136,7 +136,7 @@ class test_RemoteTlsServer server.listen(); QSignalSpy newConnection(&server, &RemoteTlsServer::newConnection); - auto config = SecureStorage::getInstance().getTlsConfigRemote().getConfiguration(); + auto config = Env::getSingleton()->getTlsConfigRemote().getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); @@ -184,7 +184,7 @@ class test_RemoteTlsServer RemoteTlsServer server; server.listen(); - auto config = SecureStorage::getInstance().getTlsConfigRemote().getConfiguration(); + auto config = Env::getSingleton()->getTlsConfigRemote().getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); config.setCaCertificates({settings.getCertificate()}); diff --git a/test/qt/remote_device/test_RemoteWebSocketServer.cpp b/test/qt/remote_device/test_RemoteWebSocketServer.cpp index 536f510..cb77b84 100644 --- a/test/qt/remote_device/test_RemoteWebSocketServer.cpp +++ b/test/qt/remote_device/test_RemoteWebSocketServer.cpp @@ -59,7 +59,7 @@ class PskHandler }; const auto& config = mWebSocket->sslConfiguration(); - const auto& pairingCiphers = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); + const auto& pairingCiphers = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); if (pairingCiphers.contains(config.sessionCipher())) { allowedErrors << QSslError::SelfSignedCertificate; @@ -164,7 +164,7 @@ class test_RemoteWebSocketServer QCOMPARE(mServer->getCurrentCertificate(), QSslCertificate()); KeyPair pair = KeyPair::generate(); QWebSocket client; - auto config = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); + auto config = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); client.setSslConfiguration(config); @@ -191,7 +191,7 @@ class test_RemoteWebSocketServer QVERIFY(mServer->listen(QStringLiteral("TestServer"))); QWebSocket client; - auto config = SecureStorage::getInstance().getTlsConfigRemote().getConfiguration(); + auto config = Env::getSingleton()->getTlsConfigRemote().getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); config.setCaCertificates({settings.getCertificate()}); @@ -230,7 +230,7 @@ class test_RemoteWebSocketServer QVERIFY(mServer->listen(QStringLiteral("TestServer"))); KeyPair pair = KeyPair::generate(); - auto config = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); + auto config = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); @@ -270,7 +270,7 @@ class test_RemoteWebSocketServer KeyPair pair = KeyPair::generate(); QWebSocket client; - auto config = SecureStorage::getInstance().getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); + auto config = Env::getSingleton()->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration(); config.setPrivateKey(pair.getKey()); config.setLocalCertificate(pair.getCertificate()); client.setSslConfiguration(config); diff --git a/test/qt/remote_device/test_ServerMessageHandler.cpp b/test/qt/remote_device/test_ServerMessageHandler.cpp index fda0934..baff3b7 100644 --- a/test/qt/remote_device/test_ServerMessageHandler.cpp +++ b/test/qt/remote_device/test_ServerMessageHandler.cpp @@ -17,10 +17,12 @@ #include "messages/IfdEstablishContextResponse.h" #include "messages/IfdEstablishPaceChannel.h" #include "messages/IfdEstablishPaceChannelResponse.h" +#include "messages/IfdModifyPinResponse.h" #include "messages/IfdStatus.h" #include "messages/IfdTransmit.h" #include "messages/IfdTransmitResponse.h" +#include "MockCardConnectionWorker.h" #include "MockDataChannel.h" #include "MockReaderManagerPlugIn.h" #include "TestFileHelper.h" @@ -30,9 +32,38 @@ Q_IMPORT_PLUGIN(MockReaderManagerPlugIn) - using namespace governikus; +Q_DECLARE_METATYPE(StatusCode) +Q_DECLARE_METATYPE(ECardApiResult::Minor) + +class MockRemoteDispatcherServer + : public RemoteDispatcherServer +{ + Q_OBJECT + QSharedPointer mMessage; + + public: + explicit MockRemoteDispatcherServer(const QSharedPointer& pDataChannel) + : RemoteDispatcherServer(pDataChannel) + { + } + + + Q_INVOKABLE void send(const QSharedPointer& pMessage) override + { + RemoteDispatcherServer::send(pMessage); + mMessage = pMessage; + } + + + QSharedPointer getMessage() + { + return mMessage; + } + + +}; class test_ServerMessageHandler : public QObject @@ -41,6 +72,7 @@ class test_ServerMessageHandler private: QSharedPointer mDataChannel; + QPointer mRemoteDispatcher; void removeReaderAndConsumeMessages(const QString& pReaderName) @@ -84,6 +116,11 @@ class test_ServerMessageHandler const auto readerManager = Env::getSingleton(); readerManager->init(); readerManager->getPlugInInfos(); // just to wait until initialization finished + + Env::setCreator(std::function& pDataChannel)>([this](const QSharedPointer ){ + mRemoteDispatcher = new MockRemoteDispatcherServer(mDataChannel); + return mRemoteDispatcher.data(); + })); } @@ -158,8 +195,15 @@ class test_ServerMessageHandler // We have a context handle: send unexpected messages and verify that an error message is sent back. sendSpy.clear(); + ReaderInfo info(QStringLiteral("NFC Reader")); + info.setMaxApduLength(500); + info.setConnected(true); + info.setBasicReader(true); + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(false); + const IfdStatus status(info); + const QByteArrayList serverMessages({ - IfdStatus("NFC Reader", PaceCapabilities(), 500, true).toByteArray(contextHandle), + status.toByteArray(contextHandle), IfdConnectResponse("NFC Reader").toByteArray(contextHandle), IfdDisconnectResponse("NFC Reader").toByteArray(contextHandle), IfdTransmitResponse("NFC Reader", "9000").toByteArray(contextHandle), @@ -600,6 +644,104 @@ class test_ServerMessageHandler } + void test_handleIfdModifyPin() + { + QSignalSpy spyLog(Env::getSingleton(), &LogHandler::fireLog); + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"InputData\": \"abcd1234\",\n" + " \"SlotHandle\": \"SlotHandle\",\n" + " \"msg\": \"IFDModifyPIN\"\n" + "}\n"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + ServerMessageHandlerImpl serverMsgHandler(mDataChannel); + QSignalSpy spyModifyPin(&serverMsgHandler, &ServerMessageHandler::fireModifyPin); + QString contextHandle; + ensureContext(contextHandle); + + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(false); + Q_EMIT mRemoteDispatcher->fireReceived(RemoteCardMessageType::IFDModifyPIN, obj, QString()); + QCOMPARE(mRemoteDispatcher->getMessage()->getType(), RemoteCardMessageType::IFDModifyPINResponse); + const auto& msg1 = mRemoteDispatcher->getMessage().staticCast(); + QCOMPARE(msg1->getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); + QCOMPARE(msg1->getSlotHandle(), "SlotHandle"); + QVERIFY(spyLog.at(3).at(0).toString().contains("ModifyPin is only available in pin pad mode.")); + + Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(true); + Q_EMIT mRemoteDispatcher->fireReceived(RemoteCardMessageType::IFDModifyPIN, obj, QString()); + const auto& msg2 = mRemoteDispatcher->getMessage().staticCast(); + QCOMPARE(msg2->getResultMinor(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); + QVERIFY(spyLog.at(5).at(0).toString().contains("Card is not connected")); + } + + + void test_SendModifyPinResponse_data() + { + QTest::addColumn("statusCode"); + QTest::addColumn("minor"); + + QTest::newRow("success") << StatusCode::SUCCESS << ECardApiResult::Minor::null; + QTest::newRow("empty") << StatusCode::EMPTY << ECardApiResult::Minor::IFDL_Terminal_NoCard; + QTest::newRow("inputTimeout") << StatusCode::INPUT_TIMEOUT << ECardApiResult::Minor::IFDL_Timeout_Error; + QTest::newRow("inputCancelled") << StatusCode::INPUT_CANCELLED << ECardApiResult::Minor::IFDL_CancellationByUser; + QTest::newRow("passwordsDiffer") << StatusCode::PASSWORDS_DIFFER << ECardApiResult::Minor::IFDL_IO_RepeatedDataMismatch; + QTest::newRow("passwordOutOfRange") << StatusCode::PASSWORD_OUTOF_RANGE << ECardApiResult::Minor::IFDL_IO_UnknownPINFormat; + QTest::newRow("default") << StatusCode::INVALID << ECardApiResult::Minor::AL_Unknown_Error; + } + + + void test_SendModifyPinResponse() + { + QFETCH(StatusCode, statusCode); + QFETCH(ECardApiResult::Minor, minor); + + ServerMessageHandlerImpl serverMsgHandler(mDataChannel); + const QString slotHandle("Slot Handle"); + const ResponseApdu apdu(statusCode); + + QString contextHandle; + ensureContext(contextHandle); + serverMsgHandler.sendModifyPinResponse(slotHandle, apdu); + QCOMPARE(mRemoteDispatcher->getMessage()->getType(), RemoteCardMessageType::IFDModifyPINResponse); + const auto& msg = mRemoteDispatcher->getMessage().staticCast(); + QCOMPARE(msg->getResultMinor(), minor); + QCOMPARE(msg->getSlotHandle(), slotHandle); + } + + + void test_EstablishPaceChannelResponse_data() + { + QTest::addColumn("returnCode"); + QTest::addColumn("minor"); + + QTest::newRow("unknown") << CardReturnCode::UNKNOWN << ECardApiResult::Minor::AL_Unknown_Error; + QTest::newRow("unknown") << CardReturnCode::CARD_NOT_FOUND << ECardApiResult::Minor::IFDL_Terminal_NoCard; + QTest::newRow("default") << CardReturnCode::OK << ECardApiResult::Minor::null; + } + + + void test_EstablishPaceChannelResponse() + { + QFETCH(CardReturnCode, returnCode); + QFETCH(ECardApiResult::Minor, minor); + + const QString slotHandle("Slot Handle"); + EstablishPaceChannelOutput output; + output.setPaceReturnCode(returnCode); + + ServerMessageHandlerImpl serverMsgHandler(mDataChannel); + QString contextHandle; + ensureContext(contextHandle); + + serverMsgHandler.sendEstablishPaceChannelResponse(slotHandle, output); + QCOMPARE(mRemoteDispatcher->getMessage()->getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); + const auto& msg = mRemoteDispatcher->getMessage().staticCast(); + QCOMPARE(msg->getResultMinor(), minor); + QCOMPARE(msg->getSlotHandle(), slotHandle); + } + + }; diff --git a/test/qt/securestorage/test_SecureStorage.cpp b/test/qt/secure_storage/test_SecureStorage.cpp similarity index 73% rename from test/qt/securestorage/test_SecureStorage.cpp rename to test/qt/secure_storage/test_SecureStorage.cpp index 05091ea..ca9fc69 100644 --- a/test/qt/securestorage/test_SecureStorage.cpp +++ b/test/qt/secure_storage/test_SecureStorage.cpp @@ -22,7 +22,6 @@ class test_SecureStorage : public QObject { Q_OBJECT - SecureStorage mSecureStorage; private: QStringList loadCommentList(const QString& pCommentName) @@ -79,10 +78,11 @@ class test_SecureStorage private Q_SLOTS: void testGetCVRootCertificatesUnique() { - static const int EXPECTED_CERTIFICATE_COUNT = 12; + const auto secureStorage = Env::getSingleton(); + static const int EXPECTED_CERTIFICATE_COUNT = 13; - QVector > cvcs = CVCertificate::fromHex(mSecureStorage.getCVRootCertificates(true)) - + CVCertificate::fromHex(mSecureStorage.getCVRootCertificates(false)); + QVector > cvcs = CVCertificate::fromHex(secureStorage->getCVRootCertificates(true)) + + CVCertificate::fromHex(secureStorage->getCVRootCertificates(false)); const int count = cvcs.count(); QCOMPARE(count, EXPECTED_CERTIFICATE_COUNT); @@ -120,7 +120,7 @@ class test_SecureStorage QTest::addColumn("commentName"); QTest::newRow("production") << 4 << true << "_comment_2"; - QTest::newRow("test") << 8 << false << "_comment_4"; + QTest::newRow("test") << 9 << false << "_comment_4"; } @@ -130,7 +130,9 @@ class test_SecureStorage QFETCH(bool, isProductive); QFETCH(QString, commentName); - QVector > cvcs = CVCertificate::fromHex(mSecureStorage.getCVRootCertificates(isProductive)); + const auto secureStorage = Env::getSingleton(); + + QVector > cvcs = CVCertificate::fromHex(secureStorage->getCVRootCertificates(isProductive)); QCOMPARE(cvcs.count(), certificateCount); const QStringList& comments = loadCommentList(commentName); @@ -173,7 +175,8 @@ class test_SecureStorage void testGetUpdateCertificate() { - const auto& certificates = mSecureStorage.getUpdateCertificates(); + const auto secureStorage = Env::getSingleton(); + const auto& certificates = secureStorage->getUpdateCertificates(); QCOMPARE(certificates.count(), 2); QFETCH(int, index); @@ -192,53 +195,66 @@ class test_SecureStorage void testGetSelfAuthentication() { - QVERIFY(mSecureStorage.getSelfAuthenticationUrl(false).isValid()); - QVERIFY(mSecureStorage.getSelfAuthenticationUrl(true).isValid()); + const auto secureStorage = Env::getSingleton(); + QVERIFY(secureStorage->getSelfAuthenticationUrl(false).isValid()); + QVERIFY(secureStorage->getSelfAuthenticationUrl(true).isValid()); } void testGetUpdateServerBaseUrl() { - QVERIFY(mSecureStorage.getUpdateServerBaseUrl().isValid()); + const auto secureStorage = Env::getSingleton(); + QVERIFY(secureStorage->getUpdateServerBaseUrl().isValid()); } void testWhitelistServerBaseUrl() { - QVERIFY(mSecureStorage.getWhitelistServerBaseUrl().isValid()); + const auto secureStorage = Env::getSingleton(); + QVERIFY(secureStorage->getWhitelistServerBaseUrl().isValid()); } void testAppcast() { - QCOMPARE(mSecureStorage.getAppcastUpdateUrl(), QUrl("https://appl.governikus-asp.de/ausweisapp2/Appcast.json")); - QCOMPARE(mSecureStorage.getAppcastBetaUpdateUrl(), QUrl("https://appl.governikus-asp.de/ausweisapp2/beta/Appcast.json")); + const auto secureStorage = Env::getSingleton(); + QCOMPARE(secureStorage->getAppcastUpdateUrl(), QUrl("https://appl.governikus-asp.de/ausweisapp2/Appcast.json")); + QCOMPARE(secureStorage->getAppcastBetaUpdateUrl(), QUrl("https://appl.governikus-asp.de/ausweisapp2/beta/Appcast.json")); } void testMinStaticKeySizes() { - QCOMPARE(mSecureStorage.getMinimumStaticKeySize(QSsl::KeyAlgorithm::Rsa), 2000); - QCOMPARE(mSecureStorage.getMinimumStaticKeySize(QSsl::KeyAlgorithm::Dsa), 2000); - QCOMPARE(mSecureStorage.getMinimumStaticKeySize(QSsl::KeyAlgorithm::Ec), 224); + const auto secureStorage = Env::getSingleton(); + QCOMPARE(secureStorage->getMinimumStaticKeySize(QSsl::KeyAlgorithm::Rsa), 2000); + QCOMPARE(secureStorage->getMinimumStaticKeySize(QSsl::KeyAlgorithm::Dsa), 2000); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + QCOMPARE(secureStorage->getMinimumStaticKeySize(QSsl::KeyAlgorithm::Dh), 2000); +#endif + QCOMPARE(secureStorage->getMinimumStaticKeySize(QSsl::KeyAlgorithm::Ec), 224); } void testMinEphemeralKeySizes() { - QCOMPARE(mSecureStorage.getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Rsa), 2000); - QCOMPARE(mSecureStorage.getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Dsa), 2000); - QCOMPARE(mSecureStorage.getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Ec), 224); + const auto secureStorage = Env::getSingleton(); + QCOMPARE(secureStorage->getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Rsa), 2000); + QCOMPARE(secureStorage->getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Dsa), 2000); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + QCOMPARE(secureStorage->getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Dh), 2000); +#endif + QCOMPARE(secureStorage->getMinimumEphemeralKeySize(QSsl::KeyAlgorithm::Ec), 224); } void testSignatureAlgorithms() { + const auto secureStorage = Env::getSingleton(); #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) QSKIP("SignatureAlgorithms not supported"); #endif - const auto& tlsSettings = mSecureStorage.getTlsConfig(); + const auto& tlsSettings = secureStorage->getTlsConfig(); QCOMPARE(tlsSettings.getSignatureAlgorithms().size(), 12); QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst(), QByteArray("RSA+SHA512")); QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast(), QByteArray("ECDSA+SHA224")); @@ -247,11 +263,12 @@ class test_SecureStorage void testSignatureAlgorithmsPsk() { + const auto secureStorage = Env::getSingleton(); #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) QSKIP("SignatureAlgorithms not supported"); #endif - const auto& tlsSettings = mSecureStorage.getTlsConfig(SecureStorage::TlsSuite::PSK); + const auto& tlsSettings = secureStorage->getTlsConfig(SecureStorage::TlsSuite::PSK); QCOMPARE(tlsSettings.getSignatureAlgorithms().size(), 4); QCOMPARE(tlsSettings.getSignatureAlgorithms().constFirst(), QByteArray("RSA+SHA512")); QCOMPARE(tlsSettings.getSignatureAlgorithms().constLast(), QByteArray("RSA+SHA224")); @@ -260,43 +277,45 @@ class test_SecureStorage void testSignatureAlgorithmsRemoteReader() { + const auto secureStorage = Env::getSingleton(); #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) QSKIP("SignatureAlgorithms not supported"); #endif - const auto& config = mSecureStorage.getTlsConfigRemote(); + const auto& config = secureStorage->getTlsConfigRemote(); QCOMPARE(config.getSignatureAlgorithms().size(), 3); - const auto& configPairing = mSecureStorage.getTlsConfigRemote(); + const auto& configPairing = secureStorage->getTlsConfigRemote(); QCOMPARE(configPairing.getSignatureAlgorithms().size(), 3); } void orderOfCiphers() { - const auto& ciphersForwardSecrecy = mSecureStorage.getTlsConfig().getCiphers(); + const auto secureStorage = Env::getSingleton(); + const auto& ciphersForwardSecrecy = secureStorage->getTlsConfig().getCiphers(); QCOMPARE(ciphersForwardSecrecy.first(), QSslCipher("ECDHE-ECDSA-AES256-GCM-SHA384")); QCOMPARE(ciphersForwardSecrecy.last(), QSslCipher("DHE-RSA-AES128-SHA256")); - const auto& ciphersPsk = mSecureStorage.getTlsConfig(SecureStorage::TlsSuite::PSK).getCiphers(); + const auto& ciphersPsk = secureStorage->getTlsConfig(SecureStorage::TlsSuite::PSK).getCiphers(); QVERIFY(ciphersPsk.count() > 0); QCOMPARE(ciphersPsk.first(), QSslCipher("RSA-PSK-AES256-GCM-SHA384")); QCOMPARE(ciphersPsk.last(), QSslCipher("RSA-PSK-AES256-CBC-SHA")); - const auto& ciphersEc = mSecureStorage.getTlsConfig().getEllipticCurves(); + const auto& ciphersEc = secureStorage->getTlsConfig().getEllipticCurves(); QCOMPARE(ciphersEc.count(), 6); QCOMPARE(ciphersEc.first(), QSslEllipticCurve::fromLongName("brainpoolP512r1")); QCOMPARE(ciphersEc.last(), QSslEllipticCurve::fromLongName("secp224r1")); - const auto& ciphersEcRemoteReader = mSecureStorage.getTlsConfigRemote().getEllipticCurves(); + const auto& ciphersEcRemoteReader = secureStorage->getTlsConfigRemote().getEllipticCurves(); QCOMPARE(ciphersEcRemoteReader.count(), 6); QCOMPARE(ciphersEcRemoteReader.first(), QSslEllipticCurve::fromLongName("brainpoolP512r1")); QCOMPARE(ciphersEcRemoteReader.last(), QSslEllipticCurve::fromLongName("secp224r1")); - const auto& ciphersEcRemoteReaderPairing = mSecureStorage.getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getEllipticCurves(); + const auto& ciphersEcRemoteReaderPairing = secureStorage->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getEllipticCurves(); QCOMPARE(ciphersEcRemoteReaderPairing.count(), 0); - const auto& ciphersRemoteReader = mSecureStorage.getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); + const auto& ciphersRemoteReader = secureStorage->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getCiphers(); QVERIFY(ciphersRemoteReader.count() > 0); QCOMPARE(ciphersRemoteReader.first(), QSslCipher("RSA-PSK-AES256-GCM-SHA384")); QCOMPARE(ciphersRemoteReader.last(), QSslCipher("RSA-PSK-AES256-CBC-SHA")); @@ -318,7 +337,9 @@ class test_SecureStorage QFETCH(SecureStorage::TlsSuite, suite); QFETCH(QSsl::SslProtocol, protocol); - QCOMPARE(mSecureStorage.getTlsConfig(suite).getProtocolVersion(), protocol); + const auto secureStorage = Env::getSingleton(); + + QCOMPARE(secureStorage->getTlsConfig(suite).getProtocolVersion(), protocol); } @@ -327,10 +348,12 @@ class test_SecureStorage QTest::addColumn("configuration"); QTest::addColumn("cipherSize"); - QTest::newRow("ciphers non PSK") << mSecureStorage.getTlsConfig().getConfiguration() << 12; - QTest::newRow("ciphers for PSK") << mSecureStorage.getTlsConfig(SecureStorage::TlsSuite::PSK).getConfiguration() << 5; - QTest::newRow("remote reader") << mSecureStorage.getTlsConfigRemote().getConfiguration() << 7; - QTest::newRow("remote reader pairing") << mSecureStorage.getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration() << 5; + const auto secureStorage = Env::getSingleton(); + + QTest::newRow("ciphers non PSK") << secureStorage->getTlsConfig().getConfiguration() << 12; + QTest::newRow("ciphers for PSK") << secureStorage->getTlsConfig(SecureStorage::TlsSuite::PSK).getConfiguration() << 5; + QTest::newRow("remote reader") << secureStorage->getTlsConfigRemote().getConfiguration() << 7; + QTest::newRow("remote reader pairing") << secureStorage->getTlsConfigRemote(SecureStorage::TlsSuite::PSK).getConfiguration() << 5; } diff --git a/test/qt/securestorage/test_TlsConfiguration.cpp b/test/qt/secure_storage/test_TlsConfiguration.cpp similarity index 100% rename from test/qt/securestorage/test_TlsConfiguration.cpp rename to test/qt/secure_storage/test_TlsConfiguration.cpp diff --git a/test/qt/services/test_AppUpdatr.cpp b/test/qt/services/test_AppUpdatr.cpp index bc182c8..4e8ed92 100644 --- a/test/qt/services/test_AppUpdatr.cpp +++ b/test/qt/services/test_AppUpdatr.cpp @@ -84,7 +84,7 @@ class test_AppUpdater } - void setJsonItemField(QJsonDocument& pDocument, QString pField, QString pValue) + void setJsonItemField(QJsonDocument& pDocument, const QString& pField, const QString& pValue) { auto itemArray = pDocument.object()["items"].toArray(); int i = 0; @@ -101,7 +101,7 @@ class test_AppUpdater } - QJsonValue getJsonItemField(QJsonDocument& pDocument, QString pField) + QJsonValue getJsonItemField(QJsonDocument& pDocument, const QString& pField) { #ifdef Q_OS_WIN QString platform = "win"; @@ -133,8 +133,8 @@ class test_AppUpdater { Env::set(Downloader::staticMetaObject, &mDownloader); - const SecureStorage& secureStorage = SecureStorage::getInstance(); - mAppCastLocation = VersionNumber::getApplicationVersion().isDeveloperVersion() ? secureStorage.getAppcastBetaUpdateUrl() : secureStorage.getAppcastUpdateUrl(); + const auto* secureStorage = Env::getSingleton(); + mAppCastLocation = VersionNumber::getApplicationVersion().isDeveloperVersion() ? secureStorage->getAppcastBetaUpdateUrl() : secureStorage->getAppcastUpdateUrl(); mJsonDocument = QJsonDocument::fromJson(test_jsonData); mReleaseNoteLocation = getJsonItemField(mJsonDocument, "notes").toString(); diff --git a/test/qt/settings/test_GeneralSettings.cpp b/test/qt/settings/test_GeneralSettings.cpp index d23a7c1..052dcd8 100644 --- a/test/qt/settings/test_GeneralSettings.cpp +++ b/test/qt/settings/test_GeneralSettings.cpp @@ -11,6 +11,8 @@ #include "GeneralSettings.h" +#include "AppSettings.h" +#include "Env.h" using namespace governikus; @@ -18,57 +20,58 @@ class test_GeneralSettings : public QObject { Q_OBJECT - QScopedPointer mSettings; private Q_SLOTS: void init() { AbstractSettings::mTestDir.clear(); - mSettings.reset(new GeneralSettings()); } void testAutoCloseWindowAfterAuthentication() { - bool initial = mSettings->isAutoCloseWindowAfterAuthentication(); + auto& settings = Env::getSingleton()->getGeneralSettings(); + bool initial = settings.isAutoCloseWindowAfterAuthentication(); bool newValue = !initial; - mSettings->setAutoCloseWindowAfterAuthentication(newValue); - QCOMPARE(mSettings->isAutoCloseWindowAfterAuthentication(), newValue); - mSettings->save(); + settings.setAutoCloseWindowAfterAuthentication(newValue); + QCOMPARE(settings.isAutoCloseWindowAfterAuthentication(), newValue); + settings.save(); - mSettings->setAutoCloseWindowAfterAuthentication(initial); - QCOMPARE(mSettings->isAutoCloseWindowAfterAuthentication(), initial); - mSettings->save(); + settings.setAutoCloseWindowAfterAuthentication(initial); + QCOMPARE(settings.isAutoCloseWindowAfterAuthentication(), initial); + settings.save(); } void testAutoCheck() { - bool initial = mSettings->isAutoUpdateCheck(); + auto& settings = Env::getSingleton()->getGeneralSettings(); + bool initial = settings.isAutoUpdateCheck(); - mSettings->setAutoUpdateCheck(!initial); - QCOMPARE(mSettings->isAutoUpdateCheck(), !initial); - mSettings->save(); + settings.setAutoUpdateCheck(!initial); + QCOMPARE(settings.isAutoUpdateCheck(), !initial); + settings.save(); - mSettings->setAutoUpdateCheck(initial); - QCOMPARE(mSettings->isAutoUpdateCheck(), initial); - mSettings->save(); + settings.setAutoUpdateCheck(initial); + QCOMPARE(settings.isAutoUpdateCheck(), initial); + settings.save(); } void testAutoStart() { #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) - bool initial = mSettings->isAutoStart(); + auto& settings = Env::getSingleton()->getGeneralSettings(); + bool initial = settings.isAutoStart(); - mSettings->setAutoStart(!initial); - QCOMPARE(mSettings->isAutoStart(), !initial); - mSettings->save(); + settings.setAutoStart(!initial); + QCOMPARE(settings.isAutoStart(), !initial); + settings.save(); - mSettings->setAutoStart(initial); - QCOMPARE(mSettings->isAutoStart(), initial); - mSettings->save(); + settings.setAutoStart(initial); + QCOMPARE(settings.isAutoStart(), initial); + settings.save(); #else QSKIP("Autostart currently only on windows and mac os"); @@ -78,142 +81,174 @@ class test_GeneralSettings void testUseScreenKeyboard() { - bool initial = mSettings->isUseScreenKeyboard(); + auto& settings = Env::getSingleton()->getGeneralSettings(); + bool initial = settings.isUseScreenKeyboard(); - mSettings->setUseScreenKeyboard(!initial); - QCOMPARE(mSettings->isUseScreenKeyboard(), !initial); - mSettings->save(); + settings.setUseScreenKeyboard(!initial); + QCOMPARE(settings.isUseScreenKeyboard(), !initial); + settings.save(); - mSettings->setUseScreenKeyboard(initial); - QCOMPARE(mSettings->isUseScreenKeyboard(), initial); - mSettings->save(); + settings.setUseScreenKeyboard(initial); + QCOMPARE(settings.isUseScreenKeyboard(), initial); + settings.save(); + } + + + void testShuffleScreenKeyboard() + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + bool initial = settings.isShuffleScreenKeyboard(); + + settings.setShuffleScreenKeyboard(!initial); + QCOMPARE(settings.isShuffleScreenKeyboard(), !initial); + settings.save(); + + settings.setShuffleScreenKeyboard(initial); + QCOMPARE(settings.isShuffleScreenKeyboard(), initial); + settings.save(); } void testDefaultValues() { - QCOMPARE(mSettings->isAutoCloseWindowAfterAuthentication(), true); - QCOMPARE(mSettings->isAutoStart(), GENERAL_SETTINGS_DEFAULT_AUTOSTART); - QCOMPARE(mSettings->isAutoUpdateCheck(), true); - QCOMPARE(mSettings->isUseScreenKeyboard(), false); - QCOMPARE(mSettings->isShowSetupAssistant(), true); - QCOMPARE(mSettings->isRemindUserToClose(), true); - QCOMPARE(mSettings->isTransportPinReminder(), true); - QCOMPARE(mSettings->getPersistentSettingsVersion(), QString()); - QCOMPARE(mSettings->isDeveloperMode(), false); - QCOMPARE(mSettings->useSelfAuthTestUri(), false); - QCOMPARE(mSettings->getLastReaderPluginType(), QString()); + auto& settings = Env::getSingleton()->getGeneralSettings(); + + QCOMPARE(settings.isAutoCloseWindowAfterAuthentication(), true); + QCOMPARE(settings.isAutoStart(), GENERAL_SETTINGS_DEFAULT_AUTOSTART); + QCOMPARE(settings.isAutoUpdateCheck(), true); + QCOMPARE(settings.isUseScreenKeyboard(), false); + QCOMPARE(settings.isShowSetupAssistant(), true); + QCOMPARE(settings.isRemindUserToClose(), true); + QCOMPARE(settings.isTransportPinReminder(), true); + QCOMPARE(settings.getPersistentSettingsVersion(), QString()); + QCOMPARE(settings.isDeveloperMode(), false); + QCOMPARE(settings.useSelfAuthTestUri(), false); + QCOMPARE(settings.getLastReaderPluginType(), QString()); } void testRemindStartupWizard() { - bool initial = mSettings->isShowSetupAssistant(); + auto& settings = Env::getSingleton()->getGeneralSettings(); + + bool initial = settings.isShowSetupAssistant(); bool newValue = !initial; - mSettings->setShowSetupAssistant(newValue); - QCOMPARE(mSettings->isShowSetupAssistant(), newValue); - mSettings->save(); + settings.setShowSetupAssistant(newValue); + QCOMPARE(settings.isShowSetupAssistant(), newValue); + settings.save(); - mSettings->setShowSetupAssistant(initial); - QCOMPARE(mSettings->isShowSetupAssistant(), initial); - mSettings->save(); + settings.setShowSetupAssistant(initial); + QCOMPARE(settings.isShowSetupAssistant(), initial); + settings.save(); } void testRemindUserToClose() { - bool initial = mSettings->isRemindUserToClose(); + auto& settings = Env::getSingleton()->getGeneralSettings(); + + bool initial = settings.isRemindUserToClose(); bool newValue = !initial; - mSettings->setRemindUserToClose(newValue); - QCOMPARE(mSettings->isRemindUserToClose(), newValue); - mSettings->save(); + settings.setRemindUserToClose(newValue); + QCOMPARE(settings.isRemindUserToClose(), newValue); + settings.save(); - mSettings->setRemindUserToClose(initial); - QCOMPARE(mSettings->isRemindUserToClose(), initial); - mSettings->save(); + settings.setRemindUserToClose(initial); + QCOMPARE(settings.isRemindUserToClose(), initial); + settings.save(); } void testPersistentSettingsVersion() { + auto& settings = Env::getSingleton()->getGeneralSettings(); + QCoreApplication::setApplicationVersion(QStringLiteral("X.Y.Z")); // the persistent settings version contains the application version number // last saving the settings --> in this test the settings have // never been saved, so the empty string is expected - QCOMPARE(mSettings->getPersistentSettingsVersion(), QString()); + QCOMPARE(settings.getPersistentSettingsVersion(), QString()); - mSettings->save(); - QCOMPARE(mSettings->getPersistentSettingsVersion(), QCoreApplication::applicationVersion()); + settings.save(); + QCOMPARE(settings.getPersistentSettingsVersion(), QCoreApplication::applicationVersion()); } void testTransportPinReminder() { - bool initialValue = mSettings->isTransportPinReminder(); + auto& settings = Env::getSingleton()->getGeneralSettings(); + + bool initialValue = settings.isTransportPinReminder(); bool newValue = !initialValue; - mSettings->save(); - mSettings->setTransportPinReminder(newValue); - QCOMPARE(mSettings->isTransportPinReminder(), newValue); - mSettings->save(); + settings.save(); + settings.setTransportPinReminder(newValue); + QCOMPARE(settings.isTransportPinReminder(), newValue); + settings.save(); - mSettings->setTransportPinReminder(initialValue); - QCOMPARE(mSettings->isTransportPinReminder(), initialValue); - mSettings->save(); + settings.setTransportPinReminder(initialValue); + QCOMPARE(settings.isTransportPinReminder(), initialValue); + settings.save(); } void testLanguage() { - const QLocale::Language initialValue = mSettings->getLanguage(); + auto& settings = Env::getSingleton()->getGeneralSettings(); + + const QLocale::Language initialValue = settings.getLanguage(); const QLocale::Language newValue = QLocale::English; - mSettings->save(); - mSettings->setLanguage(newValue); - QCOMPARE(mSettings->getLanguage(), newValue); - mSettings->save(); + settings.save(); + settings.setLanguage(newValue); + QCOMPARE(settings.getLanguage(), newValue); + settings.save(); - mSettings->setLanguage(initialValue); - QCOMPARE(mSettings->getLanguage(), initialValue); + settings.setLanguage(initialValue); + QCOMPARE(settings.getLanguage(), initialValue); } void testLastReaderPluginType() { - QString initial = mSettings->getLastReaderPluginType(); + auto& settings = Env::getSingleton()->getGeneralSettings(); + + QString initial = settings.getLastReaderPluginType(); QCOMPARE(initial, QString()); - QSignalSpy spy(mSettings.data(), &GeneralSettings::fireSettingsChanged); + QSignalSpy spy(&settings, &GeneralSettings::fireSettingsChanged); QString newValue; - mSettings->setLastReaderPluginType(newValue); + settings.setLastReaderPluginType(newValue); - QCOMPARE(mSettings->getLastReaderPluginType(), newValue); - mSettings->save(); + QCOMPARE(settings.getLastReaderPluginType(), newValue); + settings.save(); QCOMPARE(spy.count(), 0); newValue = QStringLiteral("REMOTE"); - mSettings->setLastReaderPluginType(newValue); - QCOMPARE(mSettings->getLastReaderPluginType(), newValue); + settings.setLastReaderPluginType(newValue); + QCOMPARE(settings.getLastReaderPluginType(), newValue); QCOMPARE(spy.count(), 1); - mSettings->save(); + settings.save(); } void testStoreFeedbackRequested() { + auto& settings = Env::getSingleton()->getGeneralSettings(); + #if defined(Q_OS_IOS) - QCOMPARE(mSettings->isRequestStoreFeedback(), false); + QCOMPARE(settings.isRequestStoreFeedback(), false); #else - QCOMPARE(mSettings->isRequestStoreFeedback(), false); + QCOMPARE(settings.isRequestStoreFeedback(), false); #endif - mSettings->setRequestStoreFeedback(true); - mSettings->save(); - QCOMPARE(mSettings->isRequestStoreFeedback(), true); - mSettings->setRequestStoreFeedback(false); - mSettings->save(); - QCOMPARE(mSettings->isRequestStoreFeedback(), false); + settings.setRequestStoreFeedback(true); + settings.save(); + QCOMPARE(settings.isRequestStoreFeedback(), true); + settings.setRequestStoreFeedback(false); + settings.save(); + QCOMPARE(settings.isRequestStoreFeedback(), false); } diff --git a/test/qt/settings/test_HistorySettings.cpp b/test/qt/settings/test_HistorySettings.cpp index d7718dd..97d5296 100644 --- a/test/qt/settings/test_HistorySettings.cpp +++ b/test/qt/settings/test_HistorySettings.cpp @@ -9,6 +9,9 @@ #include "HistorySettings.h" +#include "AppSettings.h" +#include "Env.h" + #include "TestFileHelper.h" #include @@ -20,64 +23,66 @@ class test_HistorySettings : public QObject { Q_OBJECT - QScopedPointer settings; private Q_SLOTS: void init() { QCoreApplication::setOrganizationName(QStringLiteral("dummy")); - AbstractSettings::mTestDir.clear(); - settings.reset(new HistorySettings()); + Env::getSingleton()->getHistorySettings().deleteSettings(); } void testEnabled() { - bool initial = settings->isEnabled(); + auto& settings = Env::getSingleton()->getHistorySettings(); + bool initial = settings.isEnabled(); - settings->setEnabled(!initial); - QCOMPARE(settings->isEnabled(), !initial); - settings->save(); + settings.setEnabled(!initial); + QCOMPARE(settings.isEnabled(), !initial); + settings.save(); - settings->setEnabled(initial); - QCOMPARE(settings->isEnabled(), initial); + settings.setEnabled(initial); + QCOMPARE(settings.isEnabled(), initial); } void testHistoryEntries() { - QVector initial = settings->getHistoryInfos(); + auto& settings = Env::getSingleton()->getHistorySettings(); + QVector initial = settings.getHistoryInfos(); HistoryInfo info("pSubjectName", "pSubjectUrl", "pUsage", QDateTime(), "pTermOfUsage", {"pRequestedData"}); QVector newValue(initial); newValue.prepend(info); // new values will be prepended, so that it appears on top - settings->addHistoryInfo(info); - QCOMPARE(settings->getHistoryInfos(), newValue); + settings.addHistoryInfo(info); + QCOMPARE(settings.getHistoryInfos(), newValue); } void testDeleteHistory() { + auto& settings = Env::getSingleton()->getHistorySettings(); HistoryInfo info("pSubjectName", "pSubjectUrl", "pUsage", QDateTime(), "pTermOfUsage", {"pRequestedData"}); - settings->addHistoryInfo(info); + settings.addHistoryInfo(info); - QCOMPARE(settings->getHistoryInfos().size(), 1); + QCOMPARE(settings.getHistoryInfos().size(), 1); - settings->deleteSettings(); + settings.deleteSettings(); - QCOMPARE(settings->getHistoryInfos().size(), 0); + QCOMPARE(settings.getHistoryInfos().size(), 0); } void testDeleteHistoryFromFile() { + auto& settings = Env::getSingleton()->getHistorySettings(); const auto file = AbstractSettings::mTestDir->path() + QStringLiteral("/dummy/Test_settings_HistorySettings.ini"); HistoryInfo info("pSubjectXYZ", "pSubjectUrlXYZ", "pUsageXYZ", QDateTime(), "pTermOfUsageXYZ", {"pRequestedDataXYZ"}); - settings->addHistoryInfo(info); - settings->addHistoryInfo(info); - settings->addHistoryInfo(info); - settings->save(); + settings.addHistoryInfo(info); + settings.addHistoryInfo(info); + settings.addHistoryInfo(info); + settings.save(); QVERIFY(QFile::exists(file)); auto content = TestFileHelper::readFile(file); @@ -87,8 +92,8 @@ class test_HistorySettings QVERIFY(content.contains("pTermOfUsageXYZ")); QVERIFY(content.contains("pRequestedDataXYZ")); - settings->deleteSettings(); - settings->save(); + settings.deleteSettings(); + settings.save(); content = TestFileHelper::readFile(file); QVERIFY(!content.contains("pSubjectXYZ")); diff --git a/test/qt/aidl/test_PskManager.cpp b/test/qt/ui/aidl/test_PskManager.cpp similarity index 100% rename from test/qt/aidl/test_PskManager.cpp rename to test/qt/ui/aidl/test_PskManager.cpp diff --git a/test/qt/jsonapi/test_Message.cpp b/test/qt/ui/json/test_Message.cpp similarity index 97% rename from test/qt/jsonapi/test_Message.cpp rename to test/qt/ui/json/test_Message.cpp index 1a52569..53f8cfb 100644 --- a/test/qt/jsonapi/test_Message.cpp +++ b/test/qt/ui/json/test_Message.cpp @@ -83,9 +83,11 @@ class test_Message auto versionInfo = VersionInfo::getInstance().toJson(QJsonDocument::Compact); const auto& result = dispatcher.processCommand(msg); - QVERIFY(result.contains(versionInfo)); - QVERIFY(result.contains("\"VersionInfo\":{")); - QVERIFY(result.contains("\"msg\":\"INFO\"")); + QCOMPARE(result, MsgType::INFO); + const QByteArray data = result; + QVERIFY(data.contains(versionInfo)); + QVERIFY(data.contains("\"VersionInfo\":{")); + QVERIFY(data.contains("\"msg\":\"INFO\"")); } diff --git a/test/qt/jsonapi/test_MsgContext.cpp b/test/qt/ui/json/test_MsgContext.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgContext.cpp rename to test/qt/ui/json/test_MsgContext.cpp diff --git a/test/qt/jsonapi/test_MsgHandler.cpp b/test/qt/ui/json/test_MsgHandler.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandler.cpp rename to test/qt/ui/json/test_MsgHandler.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerAccessRights.cpp b/test/qt/ui/json/test_MsgHandlerAccessRights.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerAccessRights.cpp rename to test/qt/ui/json/test_MsgHandlerAccessRights.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerApiLevel.cpp b/test/qt/ui/json/test_MsgHandlerApiLevel.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerApiLevel.cpp rename to test/qt/ui/json/test_MsgHandlerApiLevel.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerAuth.cpp b/test/qt/ui/json/test_MsgHandlerAuth.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerAuth.cpp rename to test/qt/ui/json/test_MsgHandlerAuth.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerCertificate.cpp b/test/qt/ui/json/test_MsgHandlerCertificate.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerCertificate.cpp rename to test/qt/ui/json/test_MsgHandlerCertificate.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerEnterCan.cpp b/test/qt/ui/json/test_MsgHandlerEnterCan.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerEnterCan.cpp rename to test/qt/ui/json/test_MsgHandlerEnterCan.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerEnterPin.cpp b/test/qt/ui/json/test_MsgHandlerEnterPin.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerEnterPin.cpp rename to test/qt/ui/json/test_MsgHandlerEnterPin.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerEnterPuk.cpp b/test/qt/ui/json/test_MsgHandlerEnterPuk.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerEnterPuk.cpp rename to test/qt/ui/json/test_MsgHandlerEnterPuk.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerInsertCard.cpp b/test/qt/ui/json/test_MsgHandlerInsertCard.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerInsertCard.cpp rename to test/qt/ui/json/test_MsgHandlerInsertCard.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerInternalError.cpp b/test/qt/ui/json/test_MsgHandlerInternalError.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerInternalError.cpp rename to test/qt/ui/json/test_MsgHandlerInternalError.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerReader.cpp b/test/qt/ui/json/test_MsgHandlerReader.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerReader.cpp rename to test/qt/ui/json/test_MsgHandlerReader.cpp diff --git a/test/qt/jsonapi/test_MsgHandlerReaderList.cpp b/test/qt/ui/json/test_MsgHandlerReaderList.cpp similarity index 100% rename from test/qt/jsonapi/test_MsgHandlerReaderList.cpp rename to test/qt/ui/json/test_MsgHandlerReaderList.cpp diff --git a/test/qt/ui/json/test_UIPlugInJson.cpp b/test/qt/ui/json/test_UIPlugInJson.cpp new file mode 100644 index 0000000..f3f90d8 --- /dev/null +++ b/test/qt/ui/json/test_UIPlugInJson.cpp @@ -0,0 +1,81 @@ +/*! + * \brief Unit tests for \ref UIPlugInJsonApi + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "UIPlugInJson.h" + +#include "LogHandler.h" + +#include +#include + +using namespace governikus; + +class test_UIPlugInJson + : public QObject +{ + Q_OBJECT + + private: + QJsonObject getJsonObject(const QByteArray& pData) + { + QJsonParseError jsonError; + const auto& json = QJsonDocument::fromJson(pData, &jsonError); + if (jsonError.error == QJsonParseError::NoError) + { + return json.object(); + } + + return QJsonObject(); + } + + + private Q_SLOTS: + void cleanup() + { + Env::getSingleton()->reset(); + } + + + void log() + { + Env::getSingleton()->init(); + const QLatin1String testDummy("Yeah, piece of cake!"); + qDebug() << testDummy; + + const QByteArray msg(R"({"cmd": "GET_LOG"})"); + QByteArray result; + UIPlugInJson api; + connect(&api, &UIPlugInJson::fireMessage, this, [&](const QByteArray& pMsg){result = pMsg;}); + api.setEnabled(); + + api.doMessageProcessing(msg); + QVERIFY(!result.isEmpty()); + QVERIFY(result.contains(R"("msg":"LOG")")); + auto json = getJsonObject(result); + QVERIFY(!json.isEmpty()); + auto data = json["data"].toString(); + QVERIFY(!data.isEmpty()); + + const QLatin1String loggedType("Process type: GET_LOG"); + QCOMPARE(data.count(loggedType), 1); + QCOMPARE(data.count(testDummy), 1); + + result.clear(); + api.doMessageProcessing(msg); + QVERIFY(!result.isEmpty()); + json = getJsonObject(result); + QVERIFY(!json.isEmpty()); + data = json["data"].toString(); + QVERIFY(!data.isEmpty()); + QCOMPARE(data.count(loggedType), 2); + QCOMPARE(data.count(testDummy), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_UIPlugInJson) +#include "test_UIPlugInJson.moc" diff --git a/test/qt/ui/qml/test_AuthModel.cpp b/test/qt/ui/qml/test_AuthModel.cpp new file mode 100644 index 0000000..b69015b --- /dev/null +++ b/test/qt/ui/qml/test_AuthModel.cpp @@ -0,0 +1,56 @@ +/*! + * \brief Unit tests for \ref AuthModel + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "AuthModel.h" + +#include "context/AuthContext.h" + +#include "paos/retrieve/DidAuthenticateEac1Parser.h" + +#include "TestFileHelper.h" + +#include +#include + + +using namespace governikus; + + +class test_AuthModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void test_ResetContext() + { + const auto model = Env::getSingleton(); + const QSharedPointer context(new AuthContext(nullptr)); + + QSignalSpy spyCurrentStateChanged(model, &WorkflowModel::fireCurrentStateChanged); + QSignalSpy spyTransactionInfoChanged(model, &AuthModel::fireTransactionInfoChanged); + + model->resetContext(context); + QCOMPARE(spyCurrentStateChanged.count(), 1); + QCOMPARE(spyTransactionInfoChanged.count(), 0); + + QByteArray content = TestFileHelper::readFile(":/paos/DIDAuthenticateEAC1.xml"); + QSharedPointer eac1(static_cast(DidAuthenticateEac1Parser().parse(content))); + context->setDidAuthenticateEac1(eac1); + QCOMPARE(model->getTransactionInfo(), QString("this is a test for TransactionInfo")); + model->resetContext(context); + QVERIFY(model->getTransactionInfo().isEmpty()); + Q_EMIT context->fireDidAuthenticateEac1Changed(); + QCOMPARE(model->getTransactionInfo(), QString("this is a test for TransactionInfo")); + QCOMPARE(spyCurrentStateChanged.count(), 2); + QCOMPARE(spyTransactionInfoChanged.count(), 3); + } + + +}; + +QTEST_GUILESS_MAIN(test_AuthModel) +#include "test_AuthModel.moc" diff --git a/test/qt/ui/qml/test_CertificateDescriptionModel.cpp b/test/qt/ui/qml/test_CertificateDescriptionModel.cpp new file mode 100644 index 0000000..c432781 --- /dev/null +++ b/test/qt/ui/qml/test_CertificateDescriptionModel.cpp @@ -0,0 +1,79 @@ +/*! + * \brief Unit tests for \ref CertificateDescriptionModel + * + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "CertificateDescriptionModel.h" + +#include "TestAuthContext.h" +#include "TestFileHelper.h" + +#include +#include + + +using namespace governikus; + + +class test_CertificateDescriptionModel + : public QObject +{ + Q_OBJECT + QSharedPointer mModel; + QSharedPointer mContext; + + private Q_SLOTS: + void init() + { + mContext.reset(new TestAuthContext(nullptr, ":/paos/DIDAuthenticateEAC1.xml")); + mModel.reset(new CertificateDescriptionModel()); + } + + + void cleanup() + { + mModel.clear(); + mContext.clear(); + } + + + void test_ResetContext() + { + QSignalSpy spy(mModel.data(), &CertificateDescriptionModel::fireChanged); + mModel->resetContext(mContext); + QCOMPARE(mModel->rowCount(), 4); + + const QString termsOfUsage = QStringLiteral("Anschrift:\nbremen online services GmbH & Co. KG\nAm Fallturm 9\n28359 Bremen\n\nE-Mail-Adresse:\nhb@bos-bremen.de\n\nZweck des Auslesevorgangs:\nDemonstration des eID-Service\n\nZust\u00E4ndige Datenschutzaufsicht:\nDie Landesbeauftragte f\u00FCr Datenschutz und Informationsfreiheit der Freien Hansestadt Bremen\nArndtstra\u00DFe 1\n27570 Bremerhaven"); + QCOMPARE(mModel->data(mModel->index(0), CertificateDescriptionModel::UserRoles::LABEL), QString("Service provider")); + QCOMPARE(mModel->data(mModel->index(0), CertificateDescriptionModel::UserRoles::TEXT), QStringLiteral("bos KG\nhttps://dev-demo.governikus-eid.de:8443")); + QCOMPARE(mModel->data(mModel->index(1), CertificateDescriptionModel::UserRoles::LABEL), QString("Certificate issuer")); + QCOMPARE(mModel->data(mModel->index(1), CertificateDescriptionModel::UserRoles::TEXT), QStringLiteral("Deutsche Post Com, Gesch\u00E4ftsfeld Signtrust\nhttp://www.signtrust.de")); + QCOMPARE(mModel->data(mModel->index(2), CertificateDescriptionModel::UserRoles::LABEL), QString("Service provider information")); + QCOMPARE(mModel->data(mModel->index(2), CertificateDescriptionModel::UserRoles::TEXT), termsOfUsage); + QCOMPARE(mModel->data(mModel->index(3), CertificateDescriptionModel::UserRoles::LABEL), QString("Validity")); + QCOMPARE(spy.count(), 1); + } + + + void test_Data() + { + mModel->resetContext(mContext); + + QCOMPARE(mModel->data(QModelIndex(), CertificateDescriptionModel::UserRoles::LABEL), QVariant()); + + const auto& rowTooLarge = mModel->index(7, 0); + QCOMPARE(mModel->data(rowTooLarge, CertificateDescriptionModel::UserRoles::LABEL), QVariant()); + + const auto& validModel = mModel->index(1, 0); + + QCOMPARE(mModel->data(validModel, CertificateDescriptionModel::UserRoles::LABEL), QString("Certificate issuer")); + QCOMPARE(mModel->data(validModel, CertificateDescriptionModel::UserRoles::TEXT), QStringLiteral("Deutsche Post Com, Gesch\u00E4ftsfeld Signtrust\nhttp://www.signtrust.de")); + QCOMPARE(mModel->data(validModel, 0), QVariant()); + } + + +}; + +QTEST_GUILESS_MAIN(test_CertificateDescriptionModel) +#include "test_CertificateDescriptionModel.moc" diff --git a/test/qt/qml/test_ChangePinModel.cpp b/test/qt/ui/qml/test_ChangePinModel.cpp similarity index 68% rename from test/qt/qml/test_ChangePinModel.cpp rename to test/qt/ui/qml/test_ChangePinModel.cpp index b02eee4..d29bdee 100644 --- a/test/qt/qml/test_ChangePinModel.cpp +++ b/test/qt/ui/qml/test_ChangePinModel.cpp @@ -20,20 +20,20 @@ class test_ChangePinModel private Q_SLOTS: void test_ResetContext() { - ChangePinModel model; + const auto& model = Env::getSingleton(); QSharedPointer context(new ChangePinContext()); - QSignalSpy resultChanged(&model, &ChangePinModel::fireResultChanged); - QSignalSpy newContextSet(&model, &ChangePinModel::fireNewContextSet); + QSignalSpy resultChanged(model, &ChangePinModel::fireResultChanged); + QSignalSpy newContextSet(model, &ChangePinModel::fireNewContextSet); - model.resetContext(); + model->resetContext(); QCOMPARE(resultChanged.count(), 1); QCOMPARE(newContextSet.count(), 0); Q_EMIT context->fireSuccessMessageChanged(); QCOMPARE(resultChanged.count(), 1); - model.resetContext(context); + model->resetContext(context); QCOMPARE(resultChanged.count(), 2); QCOMPARE(newContextSet.count(), 1); @@ -44,15 +44,15 @@ class test_ChangePinModel void test_GetResultString() { - ChangePinModel model; + const auto& model = Env::getSingleton(); QSharedPointer context(new ChangePinContext()); - QCOMPARE(model.getResultString(), QString()); + QCOMPARE(model->getResultString(), QString()); context->setStatus(GlobalStatus::Code::No_Error); context->setSuccessMessage(QStringLiteral("success")); - model.mContext = context; - QCOMPARE(model.getResultString(), QStringLiteral("success")); + model->resetContext(context); + QCOMPARE(model->getResultString(), QStringLiteral("success")); } diff --git a/test/qt/ui/qml/test_ChatModel.cpp b/test/qt/ui/qml/test_ChatModel.cpp new file mode 100644 index 0000000..3ef4f89 --- /dev/null +++ b/test/qt/ui/qml/test_ChatModel.cpp @@ -0,0 +1,213 @@ +/*! + * \brief Unit tests for \ref ChatModel + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "ChatModel.h" + +#include "context/SelfAuthContext.h" + +#include "paos/retrieve/DidAuthenticateEac1Parser.h" + +#include "MockActivationContext.h" +#include "TestFileHelper.h" + +#include +#include + + +using namespace governikus; + + +class test_ChatModel + : public QObject +{ + Q_OBJECT + QSharedPointer mModel; + QSharedPointer mActContext; + QSharedPointer mAuthContext; + + private Q_SLOTS: + void init() + { + mModel.reset(new ChatModel()); + mActContext.reset(new MockActivationContext()); + mAuthContext.reset(new AuthContext(mActContext)); + } + + + void cleanup() + { + mModel.clear(); + mActContext.clear(); + mAuthContext.clear(); + } + + + void test_ResetContext() + { + QSharedPointer selfAuthContext(new SelfAuthContext()); + + mModel->resetContext(nullptr); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG05)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG13)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG04)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG07)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG08)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG09)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG17)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG10)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG06)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG02)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG19)); + QCOMPARE(mModel->mSelectedRights, mModel->mAllRights.toSet()); + + mModel->resetContext(selfAuthContext); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG05)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG13)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG04)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG07)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG08)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG09)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG17)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG10)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG06)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG02)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG19)); + QCOMPARE(mModel->mSelectedRights, mModel->mAllRights.toSet()); + + mModel->resetContext(mAuthContext); + QVERIFY(mModel->mAllRights.isEmpty()); + QVERIFY(mModel->mOptionalRights.isEmpty()); + QVERIFY(mModel->mSelectedRights.isEmpty()); + } + + + void test_OnAuthenticationDataChanged() + { + mModel->mAuthContext = mAuthContext; + + mModel->onAuthenticationDataChanged(); + QVERIFY(mModel->mAllRights.isEmpty()); + QVERIFY(mModel->mOptionalRights.isEmpty()); + QVERIFY(mModel->mSelectedRights.isEmpty()); + + mAuthContext->mRequiredAccessRights += AccessRight::READ_DG01; + mAuthContext->mRequiredAccessRights += AccessRight::READ_DG04; + + mModel->onAuthenticationDataChanged(); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG04)); + QVERIFY(mModel->mSelectedRights.contains(AccessRight::READ_DG01)); + QVERIFY(mModel->mSelectedRights.contains(AccessRight::READ_DG04)); + QVERIFY(mModel->mOptionalRights.isEmpty()); + + mAuthContext->mOptionalAccessRights += AccessRight::READ_DG10; + mAuthContext->mOptionalAccessRights += AccessRight::READ_DG17; + + mModel->onAuthenticationDataChanged(); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG04)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG10)); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG17)); + QVERIFY(mModel->mSelectedRights.contains(AccessRight::READ_DG01)); + QVERIFY(mModel->mSelectedRights.contains(AccessRight::READ_DG04)); + QVERIFY(mModel->mSelectedRights.contains(AccessRight::READ_DG10)); + QVERIFY(mModel->mSelectedRights.contains(AccessRight::READ_DG17)); + QVERIFY(mModel->mSelectedRights.contains(AccessRight::READ_DG10)); + QVERIFY(mModel->mSelectedRights.contains(AccessRight::READ_DG17)); + } + + + void test_SetOrderedAllRights() + { + QSet rights; + rights.insert(AccessRight::READ_DG01); + rights.insert(AccessRight::INSTALL_QUAL_CERT); + rights.insert(AccessRight::CAN_ALLOWED); + mModel->setOrderedAllRights(rights); + QVERIFY(mModel->mAllRights.contains(AccessRight::READ_DG01)); + QVERIFY(mModel->mAllRights.contains(AccessRight::INSTALL_QUAL_CERT)); + QVERIFY(!mModel->mAllRights.contains(AccessRight::CAN_ALLOWED)); + } + + + void test_TransferAccessRights() + { + mAuthContext->mOptionalAccessRights += mModel->mSelectedRights += AccessRight::INSTALL_QUAL_CERT; + mAuthContext->mOptionalAccessRights += mModel->mSelectedRights += AccessRight::READ_DG01; + QVERIFY(!mAuthContext->getEffectiveAccessRights().contains(AccessRight::INSTALL_QUAL_CERT)); + QVERIFY(!mAuthContext->getEffectiveAccessRights().contains(AccessRight::READ_DG01)); + + mModel->mAuthContext = mAuthContext; + mModel->mSelectedRights += AccessRight::INSTALL_QUAL_CERT; + mModel->mSelectedRights += AccessRight::READ_DG01; + + mModel->transferAccessRights(); + QVERIFY(mAuthContext->getEffectiveAccessRights().contains(AccessRight::INSTALL_QUAL_CERT)); + QVERIFY(mAuthContext->getEffectiveAccessRights().contains(AccessRight::READ_DG01)); + } + + + void test_Data_InvalidIndex() + { + QCOMPARE(mModel->data(QModelIndex(), 1), QVariant()); + } + + + void test_DataOptionalRole() + { + QModelIndex index = mModel->createIndex(0, 0); + QList rights = {AccessRight::READ_DG01, AccessRight::READ_DG02}; + mModel->mAllRights = rights; + + QCOMPARE(mModel->data(index, ChatModel::ChatRoles::OPTIONAL_ROLE), false); + + mModel->mOptionalRights.insert(AccessRight::READ_DG01); + QCOMPARE(mModel->data(index, ChatModel::ChatRoles::OPTIONAL_ROLE), true); + } + + + void test_DataSelectedRole() + { + QSignalSpy spy(mModel.data(), &ChatModel::dataChanged); + QModelIndex index = mModel->createIndex(0, 0); + QList rights = {AccessRight::READ_DG16, AccessRight::READ_DG02}; + mModel->mAllRights = rights; + mModel->mSelectedRights = QSet(); + + QCOMPARE(mModel->data(index, ChatModel::ChatRoles::SELECTED_ROLE), false); + QVERIFY(mModel->setData(index, true, ChatModel::ChatRoles::SELECTED_ROLE)); + QVERIFY(mModel->mSelectedRights.contains(AccessRight::READ_DG16)); + QCOMPARE(spy.count(), 1); + auto arguments = spy.takeFirst(); + auto role = arguments.at(2).value >(); + QCOMPARE(arguments.at(0), index); + QCOMPARE(arguments.at(1), index); + QCOMPARE(role.at(0), ChatModel::ChatRoles::SELECTED_ROLE); + } + + + void test_DataNameRole() + { + mModel->resetContext(mAuthContext); + QModelIndex index = mModel->createIndex(0, 0); + QList rights = {AccessRight::AGE_VERIFICATION, AccessRight::READ_DG15}; + mModel->mAllRights = rights; + QByteArray content = TestFileHelper::readFile(":/paos/DIDAuthenticateEAC1.xml"); + QSharedPointer eac1(static_cast(DidAuthenticateEac1Parser().parse(content))); + mAuthContext->setDidAuthenticateEac1(eac1); + const QString requiredAge = mAuthContext->getRequiredAge(); + + QVERIFY(!requiredAge.isEmpty()); + QCOMPARE(mModel->data(index, ChatModel::ChatRoles::NAME_ROLE), "Age verification (" + requiredAge + ")"); + } + + +}; + +QTEST_GUILESS_MAIN(test_ChatModel) +#include "test_ChatModel.moc" diff --git a/test/qt/qml/test_ConnectivityManager.cpp b/test/qt/ui/qml/test_ConnectivityManager.cpp similarity index 100% rename from test/qt/qml/test_ConnectivityManager.cpp rename to test/qt/ui/qml/test_ConnectivityManager.cpp diff --git a/test/qt/ui/qml/test_HistoryModel.cpp b/test/qt/ui/qml/test_HistoryModel.cpp new file mode 100644 index 0000000..ff81f07 --- /dev/null +++ b/test/qt/ui/qml/test_HistoryModel.cpp @@ -0,0 +1,231 @@ +/*! + * \brief Unit tests for \ref HistoryModel + * + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "HistoryModel.h" + +#include "AppSettings.h" +#include "Env.h" +#include "ProviderConfiguration.h" + +#include +#include + + +using namespace governikus; + +Q_DECLARE_METATYPE(HistoryModel::HistoryRoles) + +class test_HistoryModel + : public QObject +{ + Q_OBJECT + QSharedPointer mModel; + + static ProviderConfigurationInfo createProviderInfo(const QStringList& subjectUrls) + { + if (subjectUrls.isEmpty()) + { + return ProviderConfigurationInfo(); + } + return ProviderConfigurationInfo( + /* short name */ QStringLiteral("Provider 1"), + /* long name */ QStringLiteral("Provider 1 - long name"), + /* short description */ QStringLiteral("Provider description short"), + /* long description */ QStringLiteral("Provider description long"), + /* address */ QStringLiteral("https://www.homepage.com/"), + /* homepage */ QStringLiteral("https://www.homepage.com/"), + /* category */ QStringLiteral("CategoryA"), + /* phone */ QStringLiteral("0421 123456"), + /* email */ QStringLiteral("abc@def.de"), + /* postal address */ QStringLiteral("Am Fallturm 9\n28359 Bremen"), + /* icon */ QString(), + /* image */ QString(), + /* tcTokenUrl */ QStringLiteral("https://npa.allianz.de/azservice/NpaEIDService/nparef/-wnf"), + /* clientUrl */ QStringLiteral("https://www.bva.bund.de/bafoeg-online/Bafoeg/flow/anmeld"), + /* subjectUrls */ subjectUrls); + } + + + static QVector setTestProviders(int size) + { + + QStringList subjectUrls1({QStringLiteral("https://test.test/"), QStringLiteral("https://abc.abc/"), QStringLiteral("abc.abc")}); + QStringList subjectUrls2({QStringLiteral("abc.abc")}); + QStringList subjectUrls3({QStringLiteral("https://npa.allianz.de/bla1"), QStringLiteral("https://npa.allianz.de/bla1")}); + + auto& infos = Env::getSingleton()->mProviderConfigurationInfos; + infos.clear(); + + switch (size) + { + case 1: + infos.append(createProviderInfo(subjectUrls1)); + break; + + case 3: + infos.append(createProviderInfo(subjectUrls1)); + infos.append(createProviderInfo(subjectUrls2)); + infos.append(createProviderInfo(subjectUrls3)); + } + return infos; + } + + + private Q_SLOTS: + void init() + { + mModel.reset(new HistoryModel()); + } + + + void cleanup() + { + mModel.clear(); + } + + + void test_updateConnections_data() + { + QTest::addColumn("entriesSize"); + QTest::addColumn("size"); + + QTest::newRow("empty") << 0 << 0; + QTest::newRow("2") << 2 << 4; + QTest::newRow("10") << 10 << 20; + } + + + void test_updateConnections() + { + QFETCH(int, entriesSize); + QFETCH(int, size); + + HistoryInfo info("SubjectName", "SubjectUrl", "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + QVector entries(entriesSize, info); + + Env::getSingleton()->getHistorySettings().setHistoryInfos(entries); + + mModel->updateConnections(); + QCOMPARE(mModel->mConnections.size(), size); + } + + + void test_DeterminateProviderForEmptyProviderList() + { + HistoryInfo info("SubjectName", "SubjectUrl", "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + QCOMPARE(mModel->determineProviderFor(info), ProviderConfigurationInfo()); + } + + + void test_DeterminateProviderForProviderListSize1() + { + const auto providers = setTestProviders(1); + + HistoryInfo historyInfo1("SubjectName", "", "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + QCOMPARE(mModel->determineProviderFor(historyInfo1), ProviderConfigurationInfo()); + + HistoryInfo historyInfo2("SubjectName", QString("https://test.test/"), "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + QCOMPARE(mModel->determineProviderFor(historyInfo2), providers.at(0)); + } + + + void test_DeterminateProviderForProviderListSize3() + { + const auto providers = setTestProviders(3); + + HistoryInfo historyInfo1("SubjectName", QString("https://test/"), "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + HistoryInfo historyInfo2("SubjectName", QString("https://test.test/"), "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + HistoryInfo historyInfo3("SubjectName", QString("https://abc.abc/"), "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + HistoryInfo historyInfo4("SubjectName", QString("test.test"), "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + + QCOMPARE(mModel->determineProviderFor(historyInfo1), ProviderConfigurationInfo()); + QCOMPARE(mModel->determineProviderFor(historyInfo2), providers.at(0)); + QCOMPARE(mModel->determineProviderFor(historyInfo3), providers.at(0)); + QCOMPARE(mModel->determineProviderFor(historyInfo4), ProviderConfigurationInfo()); + } + + + void test_Data_data() + { + QTest::addColumn("role"); + QTest::addColumn("result"); + + QTest::newRow("subject") << HistoryModel::HistoryRoles::SUBJECT << "SubjectName"; + QTest::newRow("purpose") << HistoryModel::HistoryRoles::PURPOSE << "Usage"; + QTest::newRow("termOfUsage") << HistoryModel::HistoryRoles::TERMSOFUSAGE << "TermOfUsage"; + QTest::newRow("requestedData") << HistoryModel::HistoryRoles::REQUESTEDDATA << "RequestedData"; + QTest::newRow("providerCategory") << HistoryModel::HistoryRoles::PROVIDER_CATEGORY << "CategoryA"; + QTest::newRow("providerShortname") << HistoryModel::HistoryRoles::PROVIDER_SHORTNAME << "Provider 1"; + QTest::newRow("providerLongname") << HistoryModel::HistoryRoles::PROVIDER_LONGNAME << "Provider 1 - long name"; + QTest::newRow("providerShortdescription") << HistoryModel::HistoryRoles::PROVIDER_SHORTDESCRIPTION << "Provider description short"; + QTest::newRow("providerLongdescription") << HistoryModel::HistoryRoles::PROVIDER_LONGDESCRIPTION << "Provider description long"; + QTest::newRow("providerAddress") << HistoryModel::HistoryRoles::PROVIDER_ADDRESS << "https://www.homepage.com/"; + QTest::newRow("providerAddressDomain") << HistoryModel::HistoryRoles::PROVIDER_ADDRESS_DOMAIN << "www.homepage.com"; + QTest::newRow("providerHomepage") << HistoryModel::HistoryRoles::PROVIDER_HOMEPAGE << "https://www.homepage.com/"; + QTest::newRow("providerHomepageBase") << HistoryModel::HistoryRoles::PROVIDER_HOMEPAGE_BASE << "www.homepage.com"; + QTest::newRow("providerPhone") << HistoryModel::HistoryRoles::PROVIDER_PHONE << "0421 123456"; + QTest::newRow("providerPhoneCost") << HistoryModel::HistoryRoles::PROVIDER_PHONE_COST << QString(); + QTest::newRow("providerPostalAddress") << HistoryModel::HistoryRoles::PROVIDER_POSTALADDRESS << "Am Fallturm 9\n28359 Bremen"; + } + + + void test_Data() + { + QFETCH(HistoryModel::HistoryRoles, role); + QFETCH(QString, result); + + HistoryInfo historyInfo("SubjectName", "https://test.test/", "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + QVector infos = {historyInfo}; + + setTestProviders(1); + + Env::getSingleton()->getHistorySettings().setHistoryInfos(infos); + QModelIndex index = mModel->createIndex(0, 0); + + QCOMPARE(mModel->data(index, role), result); + } + + + void test_RemoveRows_data() + { + QTest::addColumn("oldSize"); + QTest::addColumn("row"); + QTest::addColumn("count"); + QTest::addColumn("newSize"); + + QTest::newRow("size5") << 5 << 0 << 5 << 0; + QTest::newRow("size10") << 10 << 1 << 3 << 7; + QTest::newRow("size15") << 15 << 8 << 0 << 15; + } + + + void test_RemoveRows() + { + QFETCH(int, oldSize); + QFETCH(int, row); + QFETCH(int, count); + QFETCH(int, newSize); + + HistoryInfo historyInfo("SubjectName", "https://npa.allianz.de/bla1", "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + QVector infos(oldSize, historyInfo); + + QSignalSpy spyRemove(mModel.data(), &HistoryModel::rowsAboutToBeRemoved); + + Env::getSingleton()->getHistorySettings().setHistoryInfos(infos); + if (mModel->removeRows(row, count)) + { + QCOMPARE(Env::getSingleton()->getHistorySettings().getHistoryInfos().size(), newSize); + QList argumentsRemove = spyRemove.takeFirst(); + QCOMPARE(argumentsRemove.at(1).toInt(), row); + QCOMPARE(argumentsRemove.at(2).toInt(), row + count - 1); + } + } + + +}; + +QTEST_GUILESS_MAIN(test_HistoryModel) +#include "test_HistoryModel.moc" diff --git a/test/qt/ui/qml/test_LogModel.cpp b/test/qt/ui/qml/test_LogModel.cpp new file mode 100644 index 0000000..0d406ec --- /dev/null +++ b/test/qt/ui/qml/test_LogModel.cpp @@ -0,0 +1,147 @@ +/*! + * \brief Unit tests for \ref LogModel + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "LogModel.h" + +#include "LogHandler.h" + +#include +#include + + +using namespace governikus; + +class test_LogModel + : public QObject +{ + Q_OBJECT + QSharedPointer mModel; + + private Q_SLOTS: + void init() + { + mModel.reset(new LogModel()); + } + + + void cleanup() + { + mModel.clear(); + } + + + void test_AddLogEntry_data() + { + QTest::addColumn("input"); + QTest::addColumn("entry1"); + QTest::addColumn("entry2"); + + QTest::newRow("validEntry") << QString("input : test") << QString("input") << QString("test"); + QTest::newRow("empty") << QString(" : ") << QString() << QString(); + QTest::newRow("leftEmpty") << QString(" : test") << QString() << QString("test"); + QTest::newRow("rightEmpty") << QString("input : ") << QString("input") << QString(); + QTest::newRow("NoSpaceRight") << QString("input :test") << QString("input :test") << QString(); + QTest::newRow("NoSpaceLeft") << QString("input: test") << QString("input: test") << QString(); + QTest::newRow("invalidEntry") << QString("inputTest") << QString("inputTest") << QString(); + QTest::newRow("NoSpaces") << QString("input:test") << QString("input:test") << QString(); + QTest::newRow("emptyString") << QString() << QString() << QString(); + QTest::newRow("::") << QString("::") << QString("::") << QString(); + QTest::newRow("a : b : c") << QString("a : b : c") << QString("a") << QString("b : c"); + } + + + void test_AddLogEntry() + { + QFETCH(QString, input); + QFETCH(QString, entry1); + QFETCH(QString, entry2); + + mModel->addLogEntry(input); + QCOMPARE(mModel->mLogEntries.at(0), entry1); + QCOMPARE(mModel->mLogEntries.at(1), entry2); + } + + + void test_SetLogEntries_data() + { + QTest::addColumn("fileName"); + QTest::addColumn("logEntriesSize"); + QTest::addColumn("count"); + + QTest::newRow("empty") << QString(":/logfiles/empty.txt") << 0 << 0; + QTest::newRow("size78") << QString(":/logfiles/size78.txt") << 78 << 78; + QTest::newRow("size80") << QString(":/logfiles/size80.txt") << 80 << 80; + QTest::newRow("size82") << QString(":/logfiles/size82.txt") << 82 << 80; + QTest::newRow("size160") << QString(":/logfiles/size160.txt") << 160 << 80; + } + + + void test_SetLogEntries() + { + QFETCH(QString, fileName); + QFETCH(int, logEntriesSize); + + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QTextStream stream(&file); + + mModel->setLogEntries(stream); + QCOMPARE(mModel->mLogEntries.size(), logEntriesSize); + } + + + void test_OnNewLogMsg_data() + { + QTest::addColumn("msg"); + QTest::addColumn("fileName"); + QTest::addColumn("selectedFile"); + QTest::addColumn("newLogMsgCounter"); + QTest::addColumn("logEntriesSizeChange"); + + QTest::newRow("emptyFile_MsgAdded") << QString(" : ") << QString(":/logfiles/empty.txt") << 0 << 1 << 2; + QTest::newRow("emptyFile_MsgNotAdded") << QString(" : ") << QString(":/logfiles/empty.txt") << 1 << 0 << 0; + QTest::newRow("emptyFile_MsgAdded_ViewChanged") << QString("test : input") << QString(":/logfiles/empty.txt") << 0 << 1 << 2; + + QTest::newRow("MsgAdded_Size78") << QString() << QString(":/logfiles/size78.txt") << 0 << 1 << 2; + QTest::newRow("MsgAdded_ViewChanged_Size78") << QString("test : input") << QString(":/logfiles/size78.txt") << 0 << 1 << 2; + QTest::newRow("MsgNotAdded_Size78") << QString("test : input") << QString(":/logfiles/size78.txt") << 2 << 0 << 0; + + QTest::newRow("MsgAdded_Size80") << QString() << QString(":/logfiles/size80.txt") << 0 << 1 << 2; + QTest::newRow("MsgNotAdded_Size80") << QString("test : input") << QString(":/logfiles/size80.txt") << 1 << 0 << 0; + QTest::newRow("MsgAdded_ViewChanged_Size80") << QString("test : input") << QString(":/logfiles/size80.txt") << 0 << 1 << 2; + + QTest::newRow("MsgAdded_Size82") << QString("test : input") << QString(":/logfiles/size82.txt") << 0 << 1 << 2; + QTest::newRow("MsgNotAdded_Size82") << QString(" : ") << QString(":/logfiles/size82.txt") << 3 << 0 << 0; + QTest::newRow("MsgAdded_ViewChanged_Size82") << QString("test : input") << QString(":/logfiles/size82.txt") << 0 << 1 << 2; + } + + + void test_OnNewLogMsg() + { + QFETCH(QString, msg); + QFETCH(QString, fileName); + QFETCH(int, selectedFile); + QFETCH(int, newLogMsgCounter); + QFETCH(int, logEntriesSizeChange); + + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QTextStream stream(&file); + mModel->setLogEntries(stream); + int oldSize = mModel->mLogEntries.size(); + mModel->mSelectedLogFile = selectedFile; + QSignalSpy spyNewLogMsg(mModel.data(), &LogModel::fireNewLogMsg); + + mModel->onNewLogMsg(msg); + QCOMPARE(spyNewLogMsg.count(), newLogMsgCounter); + QCOMPARE(mModel->mLogEntries.size(), oldSize + logEntriesSizeChange); + } + + +}; + +QTEST_GUILESS_MAIN(test_LogModel) +#include "test_LogModel.moc" diff --git a/test/qt/ui/qml/test_NotificationModel.cpp b/test/qt/ui/qml/test_NotificationModel.cpp new file mode 100644 index 0000000..9b4aaef --- /dev/null +++ b/test/qt/ui/qml/test_NotificationModel.cpp @@ -0,0 +1,95 @@ +/*! + * \brief Unit tests for \ref NotificationModel + * + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + +#include "NotificationModel.h" + + +#include +#include + +using namespace governikus; + +class test_NotificationModel + : public QObject +{ + + Q_OBJECT + QSharedPointer mModel; + + private Q_SLOTS: + void init() + { + mModel.reset(new NotificationModel()); + } + + + void cleanup() + { + mModel.clear(); + } + + + void test_OnNewLogMsg() + { + QSignalSpy spy(mModel.data(), &NotificationModel::fireLastTypeChanged); + + const QString msg("message"); + const QString developermode("developermode"); + for (int i = 0; i < 20; i++) + { + mModel->onNewLogMsg(msg, developermode); + QCOMPARE(mModel->mNotificationEntries.at(i).mText, msg); + QCOMPARE(mModel->mNotificationEntries.at(i).mType, developermode); + } + const QString newMsg("new message"); + const QString feedback("feedback"); + mModel->onNewLogMsg(newMsg, feedback); + QCOMPARE(mModel->mNotificationEntries.at(20).mText, newMsg); + QCOMPARE(mModel->mNotificationEntries.at(20).mType, feedback); + QCOMPARE(spy.count(), 21); + } + + + void test_Data_data() + { + QTest::addColumn("row"); + QTest::addColumn("size"); + QTest::addColumn("role"); + QTest::addColumn("output"); + + int type = NotificationModel::UserRoles::TYPE; + int time = NotificationModel::UserRoles::TIME; + + QTest::newRow("entriesEmpty") << 0 << 0 << 0 << QVariant(); + QTest::newRow("RowNumberEqualsSize") << 2 << 2 << type << QVariant(); + QTest::newRow("entriesFirstIndex0Type") << 4 << 5 << type << QVariant(QString("developermode")); + QTest::newRow("entriesFirstIndex2Text") << 3 << 22 << 5 << QVariant(QString("message")); + QTest::newRow("indexOutOfRange") << 10 << 5 << time << QVariant(); + } + + + void test_Data() + { + QFETCH(int, row); + QFETCH(int, size); + QFETCH(int, role); + QFETCH(QVariant, output); + + QModelIndex index = mModel->createIndex(row, 0); + const QString msg("message"); + const QString developermode("developermode"); + for (int i = 0; i < size; i++) + { + mModel->onNewLogMsg(msg, developermode); + } + QCOMPARE(mModel->data(index, role), output); + } + + +}; + +QTEST_GUILESS_MAIN(test_NotificationModel) +#include "test_NotificationModel.moc" diff --git a/test/qt/ui/qml/test_NumberModel.cpp b/test/qt/ui/qml/test_NumberModel.cpp new file mode 100644 index 0000000..a358a90 --- /dev/null +++ b/test/qt/ui/qml/test_NumberModel.cpp @@ -0,0 +1,424 @@ +/*! + * \brief Unit tests for \ref NumberModel + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "NumberModel.h" + +#include "context/ChangePinContext.h" +#include "context/RemoteServiceContext.h" + +#include "MockCardConnectionWorker.h" + +#include +#include + +using namespace governikus; + + +class test_NumberModel + : public QObject +{ + Q_OBJECT + NumberModel * mModel; + + private Q_SLOTS: + void init() + { + mModel = Env::getSingleton(); + } + + + void cleanup() + { + mModel->resetContext(); + } + + + void cleanupTestCase() + { + mModel->deleteLater(); + } + + + void test_ResetContext() + { + const QSharedPointer pinContext(new ChangePinContext()); + const QSharedPointer context(new WorkflowContext()); + + QSignalSpy spyCanChanged(mModel, &NumberModel::fireCanChanged); + QSignalSpy spyPinChanged(mModel, &NumberModel::firePinChanged); + QSignalSpy spyNewPinChanged(mModel, &NumberModel::fireNewPinChanged); + QSignalSpy spyCanAllowed(mModel, &NumberModel::fireCanAllowedModeChanged); + QSignalSpy spyPukChanged(mModel, &NumberModel::firePukChanged); + QSignalSpy spyReaderNameChanged(mModel, &NumberModel::fireReaderInfoChanged); + QSignalSpy spyLastPaceResultChanged(mModel, &NumberModel::fireInputErrorChanged); + + mModel->resetContext(); + QCOMPARE(spyCanChanged.count(), 1); + QCOMPARE(spyPinChanged.count(), 1); + QCOMPARE(spyNewPinChanged.count(), 1); + QCOMPARE(spyCanAllowed.count(), 1); + QCOMPARE(spyPukChanged.count(), 1); + QCOMPARE(spyReaderNameChanged.count(), 1); + QCOMPARE(spyLastPaceResultChanged.count(), 1); + + mModel->resetContext(context); + QCOMPARE(spyCanChanged.count(), 2); + QCOMPARE(spyPinChanged.count(), 2); + QCOMPARE(spyNewPinChanged.count(), 2); + QCOMPARE(spyCanAllowed.count(), 2); + QCOMPARE(spyPukChanged.count(), 2); + QCOMPARE(spyReaderNameChanged.count(), 2); + QCOMPARE(spyLastPaceResultChanged.count(), 2); + Q_EMIT context->fireCanChanged(); + QCOMPARE(spyCanChanged.count(), 3); + Q_EMIT context->firePinChanged(); + QCOMPARE(spyPinChanged.count(), 3); + Q_EMIT context->fireCanAllowedModeChanged(); + QCOMPARE(spyCanAllowed.count(), 3); + Q_EMIT context->fireReaderNameChanged(); + QCOMPARE(spyReaderNameChanged.count(), 3); + Q_EMIT context->firePaceResultUpdated(); + QCOMPARE(spyLastPaceResultChanged.count(), 4); + + mModel->resetContext(pinContext); + QCOMPARE(spyCanChanged.count(), 4); + QCOMPARE(spyPinChanged.count(), 4); + QCOMPARE(spyNewPinChanged.count(), 3); + QCOMPARE(spyCanAllowed.count(), 4); + QCOMPARE(spyPukChanged.count(), 3); + QCOMPARE(spyReaderNameChanged.count(), 4); + QCOMPARE(spyLastPaceResultChanged.count(), 5); + + Q_EMIT pinContext->fireCanChanged(); + QCOMPARE(spyCanChanged.count(), 5); + Q_EMIT pinContext->firePinChanged(); + QCOMPARE(spyPinChanged.count(), 5); + Q_EMIT pinContext->fireCanAllowedModeChanged(); + QCOMPARE(spyCanAllowed.count(), 5); + Q_EMIT pinContext->fireReaderNameChanged(); + QCOMPARE(spyReaderNameChanged.count(), 5); + Q_EMIT pinContext->firePaceResultUpdated(); + QCOMPARE(spyLastPaceResultChanged.count(), 7); + Q_EMIT pinContext->fireNewPinChanged(); + QCOMPARE(spyNewPinChanged.count(), 4); + Q_EMIT pinContext->firePukChanged(); + QCOMPARE(spyPukChanged.count(), 4); + } + + + void test_Can() + { + const QSharedPointer context(new WorkflowContext()); + const QString can = QStringLiteral("111111"); + + mModel->setCan(can); + QCOMPARE(mModel->getCan(), QString()); + + mModel->resetContext(context); + mModel->setCan(can); + QCOMPARE(mModel->getCan(), can); + QCOMPARE(context->getCan(), can); + } + + + void test_Pin() + { + const QSharedPointer context(new WorkflowContext()); + const QString pin = QStringLiteral("111111"); + mModel->setPin(pin); + QCOMPARE(mModel->getPin(), QString()); + + mModel->resetContext(context); + mModel->setPin(pin); + QCOMPARE(mModel->getPin(), pin); + QCOMPARE(context->getPin(), pin); + } + + + void test_NewPin() + { + const QSharedPointer context(new WorkflowContext()); + const QSharedPointer changePinContext(new ChangePinContext()); + const QSharedPointer remoteServiceContext(new RemoteServiceContext()); + const QString pin = QStringLiteral("111111"); + + mModel->resetContext(context); + mModel->setNewPin(pin); + QCOMPARE(mModel->getNewPin(), QString()); + + mModel->resetContext(changePinContext); + mModel->setNewPin(pin); + QCOMPARE(changePinContext->getNewPin(), pin); + QCOMPARE(mModel->getNewPin(), pin); + + mModel->resetContext(remoteServiceContext); + mModel->setNewPin(pin); + QCOMPARE(remoteServiceContext->getNewPin(), pin); + QCOMPARE(mModel->getNewPin(), QString()); + } + + + void test_Puk() + { + const QSharedPointer context(new WorkflowContext()); + const QString puk = QStringLiteral("111111"); + + mModel->resetContext(context); + mModel->setPuk(puk); + QCOMPARE(context->getPuk(), puk); + QCOMPARE(mModel->getPuk(), puk); + } + + + void test_OnReaderInfoChanged() + { + const QSharedPointer context(new WorkflowContext()); + QSignalSpy spyReaderNameChanged(mModel, &NumberModel::fireReaderInfoChanged); + + const QString readerName = QStringLiteral("name"); + const QString test = QStringLiteral("test"); + + const auto readerManager = Env::getSingleton(); + Q_EMIT readerManager->fireReaderPropertiesUpdated(QString()); + QCOMPARE(spyReaderNameChanged.count(), 0); + + context->setReaderName(test); + mModel->resetContext(context); + Q_EMIT readerManager->fireReaderPropertiesUpdated(QString()); + QCOMPARE(spyReaderNameChanged.count(), 1); + + context->setReaderName(readerName); + mModel->resetContext(context); + Q_EMIT readerManager->fireReaderPropertiesUpdated(QString()); + QCOMPARE(spyReaderNameChanged.count(), 3); + } + + + void test_CanAllowed() + { + const QSharedPointer context(new WorkflowContext()); + QVERIFY(!mModel->isCanAllowedMode()); + + mModel->resetContext(context); + QVERIFY(!mModel->isCanAllowedMode()); + + context->setCanAllowedMode(true); + QVERIFY(mModel->isCanAllowedMode()); + } + + + void test_PinDeactivatedFalse() + { + QVERIFY(!mModel->isPinDeactivated()); + + const QSharedPointer context(new WorkflowContext()); + mModel->resetContext(context); + QVERIFY(!mModel->isPinDeactivated()); + } + + + void test_Error() + { + QThread connectionThread; + connectionThread.start(); + + const QSharedPointer context(new WorkflowContext()); + + QCOMPARE(mModel->getInputError(), QString()); + QVERIFY(!mModel->hasError()); + + context->setLastPaceResult(CardReturnCode::OK); + mModel->resetContext(context); + QCOMPARE(mModel->getInputError(), QString()); + QVERIFY(!mModel->hasError()); + + context->setLastPaceResult(CardReturnCode::CANCELLATION_BY_USER); + QCOMPARE(mModel->getInputError(), QString()); + QVERIFY(!mModel->hasError()); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&connectionThread); + const QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + + context->setLastPaceResult(CardReturnCode::INVALID_PIN); + QCOMPARE(mModel->getInputError(), tr("The given PIN is not correct. You have 2 tries to enter the correct PIN.")); + QVERIFY(mModel->hasError()); + + context->setLastPaceResult(CardReturnCode::INVALID_PIN_2); + QCOMPARE(mModel->getInputError(), tr("You have entered the wrong PIN twice. " + "Prior to a third attempt, you have to enter your six-digit card access number first. " + "You can find your card access number on the front of your ID card.")); + QVERIFY(mModel->hasError()); + + context->setLastPaceResult(CardReturnCode::INVALID_PIN_3); + QCOMPARE(mModel->getInputError(), tr("You have entered a wrong PIN three times. " + "Your PIN is now blocked. " + "You have to enter the PUK now for unblocking.")); + QVERIFY(mModel->hasError()); + + context->setLastPaceResult(CardReturnCode::INVALID_CAN); + QCOMPARE(mModel->getInputError(), tr("You have entered a wrong CAN, please try again.")); + + context->setLastPaceResult(CardReturnCode::INVALID_PUK); + QCOMPARE(mModel->getInputError(), tr("You have entered a wrong PUK. " + "Please try again.")); + QVERIFY(mModel->hasError()); + + context->setLastPaceResult(CardReturnCode::UNKNOWN); + QCOMPARE(mModel->getInputError(), tr("An unexpected error has occurred during processing.")); + + context->setLastPaceResult(CardReturnCode::UNEXPECTED_TRANSMIT_STATUS); + QCOMPARE(mModel->getInputError(), QStringLiteral("%1 %3.").arg( + tr("A protocol error occurred. Please make sure that your ID card is placed correctly on the card reader and try again. If the problem occurs again, please contact our support at"), + tr("https://www.ausweisapp.bund.de/en/qa/support/"), + tr("AusweisApp2 Support"))); + QVERIFY(mModel->hasError()); + + connectionThread.quit(); + connectionThread.wait(); + } + + + void test_GetRetryCounter() + { + QThread connectionThread; + connectionThread.start(); + + const QSharedPointer context(new WorkflowContext()); + + QCOMPARE(mModel->getRetryCounter(), -1); + + mModel->resetContext(context); + QCOMPARE(mModel->getRetryCounter(), -1); + + const QString name = QStringLiteral("name"); + const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer(), + 3, true, false); + MockReader reader(name); + reader.getReaderInfo().setCardInfo(cardInfo); + const QSharedPointer worker(new MockCardConnectionWorker(&reader)); + worker->moveToThread(&connectionThread); + const QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + QCOMPARE(mModel->getRetryCounter(), 3); + + connectionThread.quit(); + connectionThread.wait(); + } + + + void test_RequestTransportPin_data() + { + QTest::addColumn >("context"); + QTest::addColumn("contextRequestedTransportPin"); + + QTest::newRow("WorkflowContext") << QSharedPointer(new WorkflowContext()) << false; + QTest::newRow("ChangePinContext") << QSharedPointer(new ChangePinContext()) << false; + QTest::newRow("ChangePinContext-false") << QSharedPointer(new ChangePinContext(false)) << false; + QTest::newRow("ChangePinContext-true") << QSharedPointer(new ChangePinContext(true)) << true; + } + + + void test_RequestTransportPin() + { + QFETCH(QSharedPointer, context); + QFETCH(bool, contextRequestedTransportPin); + + QVERIFY(!mModel->isRequestTransportPin()); + + mModel->setRequestTransportPin(true); + QVERIFY(mModel->isRequestTransportPin()); + mModel->setRequestTransportPin(false); + QVERIFY(!mModel->isRequestTransportPin()); + + mModel->resetContext(context); + QCOMPARE(mModel->isRequestTransportPin(), contextRequestedTransportPin); + + mModel->setRequestTransportPin(true); + QVERIFY(mModel->isRequestTransportPin()); + + mModel->setRequestTransportPin(false); + QVERIFY(!mModel->isRequestTransportPin()); + mModel->setRequestTransportPin(true); + QVERIFY(mModel->isRequestTransportPin()); + + mModel->resetContext(); + QVERIFY(!mModel->isRequestTransportPin()); + } + + + void test_GetPasswordType_ContextIsNullptr() + { + QCOMPARE(mModel->getPasswordType(), NumberModel::QmlPasswordType::PASSWORD_PIN); + } + + + void test_GetPasswordType_RequestNewPin() + { + const QSharedPointer context(new WorkflowContext()); + + mModel->resetContext(context); + mModel->requestNewPin(); + + QCOMPARE(mModel->getPasswordType(), NumberModel::QmlPasswordType::PASSWORD_NEW_PIN); + } + + + void test_GetPasswordType_data() + { + QTest::addColumn("passwordId"); + QTest::addColumn("passwordType"); + + QTest::newRow("unknown") << PacePasswordId::UNKNOWN << NumberModel::QmlPasswordType::PASSWORD_PIN; + QTest::newRow("mrz") << PacePasswordId::PACE_MRZ << NumberModel::QmlPasswordType::PASSWORD_PIN; + QTest::newRow("pin") << PacePasswordId::PACE_PIN << NumberModel::QmlPasswordType::PASSWORD_PIN; + QTest::newRow("can") << PacePasswordId::PACE_CAN << NumberModel::QmlPasswordType::PASSWORD_CAN; + QTest::newRow("puk") << PacePasswordId::PACE_PUK << NumberModel::QmlPasswordType::PASSWORD_PUK; + } + + + void test_GetPasswordType() + { + QFETCH(PacePasswordId, passwordId); + QFETCH(NumberModel::QmlPasswordType, passwordType); + + const QSharedPointer context(new WorkflowContext()); + + mModel->resetContext(context); + + context->setEstablishPaceChannelType(passwordId); + QCOMPARE(mModel->getPasswordType(), passwordType); + } + + + void test_OnCardConnectionChanged() + { + QThread connectionThread; + connectionThread.start(); + + const QSharedPointer context(new WorkflowContext()); + + mModel->resetContext(context); + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&connectionThread); + const QSharedPointer connection(new CardConnection(worker)); + context->setCardConnection(connection); + QSignalSpy spy(mModel, &NumberModel::fireReaderInfoChanged); + + Q_EMIT context->fireCardConnectionChanged(); + Q_EMIT connection->fireReaderInfoChanged(ReaderInfo()); + QCOMPARE(spy.count(), 3); + + connectionThread.quit(); + connectionThread.wait(); + } + + +}; + +QTEST_GUILESS_MAIN(test_NumberModel) +#include "test_NumberModel.moc" diff --git a/test/qt/ui/qml/test_ProviderCategoryFilterModel.cpp b/test/qt/ui/qml/test_ProviderCategoryFilterModel.cpp new file mode 100644 index 0000000..7ca5bea --- /dev/null +++ b/test/qt/ui/qml/test_ProviderCategoryFilterModel.cpp @@ -0,0 +1,212 @@ +/*! + * \brief Unit tests for \ref ProviderCategoryFilterModel + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "ProviderCategoryFilterModel.h" + +#include + +using namespace governikus; + +class MockProviderModel + : public QAbstractListModel +{ + Q_OBJECT + QMap mData; + + public: + explicit MockProviderModel() + : mData() + { + } + + + int rowCount(const QModelIndex& parent) const override + { + Q_UNUSED(parent); + return 1; + } + + + QVariant data(const QModelIndex& index, int role) const override + { + Q_UNUSED(index); + return mData.value(role); + } + + + QModelIndex index(int row, int column, const QModelIndex& parent) const override + { + Q_UNUSED(parent); + QModelIndex index = createIndex(row, column); + return index; + } + + + QModelIndex parent(const QModelIndex& pIndex) const override + { + return pIndex.parent(); + } + + + int columnCount(const QModelIndex& parent) const override + { + Q_UNUSED(parent); + return 1; + } + + + void addData(int pRole, const QVariant& pValue) + { + mData.insert(pRole, pValue); + } + + +}; + + +class test_ProviderCategoryFilterModel + : public QObject +{ + Q_OBJECT + QSharedPointer mModel; + + private Q_SLOTS: + void init() + { + mModel.reset(new ProviderCategoryFilterModel()); + } + + + void cleanup() + { + mModel.reset(); + } + + + void test_UpdateSearchString() + { + QSignalSpy spy(mModel.data(), &ProviderCategoryFilterModel::fireCriteriaChanged); + const QString search = QStringLiteral("search"); + + mModel->updateSearchString(search); + QCOMPARE(mModel->getSearchString(), search); + QCOMPARE(spy.count(), 1); + } + + + void test_CategorySelection() + { + QSignalSpy spy(mModel.data(), &ProviderCategoryFilterModel::fireCriteriaChanged); + const QString category1 = QStringLiteral("CATEGORY"); + const QString category2 = QStringLiteral("testCATEGORY"); + + mModel->setCategorySelection(QString()); + QVERIFY(mModel->mSelectedCategories.isEmpty()); + QCOMPARE(spy.count(), 1); + + mModel->setCategorySelection(category1); + QVERIFY(mModel->mSelectedCategories.contains("category")); + QCOMPARE(spy.count(), 2); + + mModel->updateCategorySelection(category2, true); + QVERIFY(mModel->mSelectedCategories.contains("testcategory")); + QCOMPARE(spy.count(), 3); + + mModel->updateCategorySelection(category2, true); + QVERIFY(mModel->mSelectedCategories.contains("testcategory")); + QCOMPARE(spy.count(), 3); + + mModel->updateCategorySelection(category1, false); + QVERIFY(mModel->mSelectedCategories.contains("testcategory")); + QVERIFY(!mModel->mSelectedCategories.contains("category")); + QCOMPARE(spy.count(), 4); + } + + + void test_SortByCategoryFirst() + { + mModel->sortByCategoryFirst(true); + QCOMPARE(mModel->sortRole(), ProviderModel::SORT_ROLE); + + mModel->sortByCategoryFirst(false); + QCOMPARE(mModel->sortRole(), ProviderModel::SHORTNAME); + } + + + void test_AddAdditionalResultCategories() + { + QSignalSpy spy(mModel.data(), &ProviderCategoryFilterModel::fireCriteriaChanged); + + mModel->addAdditionalResultCategories(); + QVERIFY(mModel->getSelectedCategories().isEmpty()); + QCOMPARE(spy.count(), 0); + + const QString category("citizen"); + const QString selected("other"); + const QString searchString("provider1"); + mModel->setCategorySelection(selected); + mModel->updateSearchString(searchString); + MockProviderModel model; + model.addData(Qt::DisplayRole, searchString); + model.addData(ProviderModel::CATEGORY, category); + mModel->setSourceModel(&model); + + mModel->addAdditionalResultCategories(); + QVERIFY(mModel->getSelectedCategories().contains(category)); + QCOMPARE(spy.count(), 3); + } + + + void test_ResultCountForFilter() + { + const QSet categories({"citizen"}); + const QString selected("other"); + const QString searchString("provider1"); + + QCOMPARE(mModel->resultCountForFilter(categories, searchString), 0); + + mModel->setCategorySelection(selected); + mModel->updateSearchString(searchString); + MockProviderModel model; + model.addData(Qt::DisplayRole, searchString); + model.addData(ProviderModel::CATEGORY, "citizen"); + mModel->setSourceModel(&model); + QCOMPARE(mModel->resultCountForFilter(categories, searchString), 1); + + const QSet unknownCategories({"test category"}); + QCOMPARE(mModel->resultCountForFilter(unknownCategories, searchString), 0); + } + + + void test_FilterAcceptsRow() + { + QVERIFY(mModel->filterAcceptsRow(0, QModelIndex())); + + const QString all("all"); + mModel->setCategorySelection(all); + QVERIFY(mModel->filterAcceptsRow(0, QModelIndex())); + + const QString citizen("citizen"); + mModel->setCategorySelection(citizen); + MockProviderModel model; + model.addData(ProviderModel::CATEGORY, citizen); + mModel->setSourceModel(&model); + QVERIFY(mModel->filterAcceptsRow(0, QModelIndex())); + + const QString unknownCategory("test category"); + mModel->setCategorySelection(unknownCategory); + QVERIFY(!mModel->filterAcceptsRow(0, QModelIndex())); + + const QString searchString("search"); + mModel->updateSearchString(searchString); + QVERIFY(!mModel->filterAcceptsRow(0, QModelIndex())); + } + + +}; + +QTEST_GUILESS_MAIN(test_ProviderCategoryFilterModel) +#include "test_ProviderCategoryFilterModel.moc" diff --git a/test/qt/ui/qml/test_ProviderModel.cpp b/test/qt/ui/qml/test_ProviderModel.cpp new file mode 100644 index 0000000..01225fd --- /dev/null +++ b/test/qt/ui/qml/test_ProviderModel.cpp @@ -0,0 +1,69 @@ +/*! + * \brief Unit tests for \ref ProviderModel + * + * \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "ProviderModel.h" + +#include +#include + + +using namespace governikus; + + +class test_ProviderModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void createCostStringNullCost() + { + const auto& msg = ProviderModel::createCostString(CallCost()); + QVERIFY(msg.isNull()); + } + + + void createCostString_data() + { + QTest::addColumn("freeSeconds"); + QTest::addColumn("lLineCentsPMin"); + QTest::addColumn("lLineCentsPCall"); + QTest::addColumn("mobCentsPMin"); + QTest::addColumn("mobCentsPCall"); + QTest::addColumn("msg"); + + const QString msg1 = QStringLiteral("10 seconds free, afterwards landline costs 0.5 ct/min; mobile costs may vary."); + const QString msg2 = QStringLiteral("landline costs 20 ct/call; mobile costs may vary."); + const QString msg3 = QStringLiteral("landline costs 15 ct/min; mobile costs 25 ct/min"); + const QString msg4 = QStringLiteral("20 seconds free, afterwards landline costs 15 ct/min; mobile costs 1.01 EUR/call"); + const QString msg5 = QStringLiteral("60 seconds free, afterwards landline costs 2 EUR/call; mobile costs 55 ct/call"); + + QTest::newRow("landlineProMin") << 10 << 0.5 << 2.4 << 0.0 << 0.0 << msg1; + QTest::newRow("landlineProCall") << 0 << 0.0 << 20.0 << 0.0 << 0.0 << msg2; + QTest::newRow("landlineAndMobProMin") << 0 << 15.0 << 30.0 << 25.0 << 0.0 << msg3; + QTest::newRow("landlineProMinMobProCall") << 20 << 15.0 << 30.0 << 0.0 << 101.0 << msg4; + QTest::newRow("landlineAndMobProCall") << 60 << 0.0 << 200.0 << 0.0 << 55.0 << msg5; + } + + + void createCostString() + { + QFETCH(int, freeSeconds); + QFETCH(double, lLineCentsPMin); + QFETCH(double, lLineCentsPCall); + QFETCH(double, mobCentsPMin); + QFETCH(double, mobCentsPCall); + QFETCH(QString, msg); + + const CallCost cost(freeSeconds, lLineCentsPMin, lLineCentsPCall, mobCentsPMin, mobCentsPCall); + QCOMPARE(ProviderModel::createCostString(cost), msg); + } + + +}; + +QTEST_GUILESS_MAIN(test_ProviderModel) +#include "test_ProviderModel.moc" diff --git a/test/qt/ui/qml/test_ProviderNameFilterModel.cpp b/test/qt/ui/qml/test_ProviderNameFilterModel.cpp new file mode 100644 index 0000000..1642547 --- /dev/null +++ b/test/qt/ui/qml/test_ProviderNameFilterModel.cpp @@ -0,0 +1,79 @@ +/*! + * \brief Unit tests for \ref ProviderNameFilterModel + * + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ +#include "ProviderNameFilterModel.h" + +#include "AppSettings.h" +#include "HistoryModel.h" +#include "ProviderConfiguration.h" +#include "ResourceLoader.h" + +#include + + +using namespace governikus; + + +class test_ProviderNameFilterModel + : public QObject +{ + Q_OBJECT + QSharedPointer mModel; + + private Q_SLOTS: + void initTestCase() + { + ResourceLoader::getInstance().init(); + } + + + void init() + { + mModel.reset(new ProviderNameFilterModel()); + } + + + void cleanup() + { + mModel.clear(); + } + + + void test_FilterAcceptsRow() + { + QVERIFY(!mModel->filterAcceptsRow(0, QModelIndex())); + + HistoryModel model; + mModel->setSourceModel(&model); + + HistoryInfo historyInfo1("SubjectName", QString("https://makler.allianz.de"), "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + HistoryInfo historyInfo2("SubjectName", QString("https://test.de"), "Usage", QDateTime::currentDateTime(), "TermOfUsage", {"RequestedData"}); + Env::getSingleton()->getHistorySettings().addHistoryInfo(historyInfo1); + Env::getSingleton()->getHistorySettings().addHistoryInfo(historyInfo2); + const QString providerAddress("https://makler.allianz.de"); + mModel->setProviderAddress(providerAddress); + + QVERIFY(!mModel->filterAcceptsRow(0, QModelIndex())); + QVERIFY(mModel->filterAcceptsRow(1, QModelIndex())); + } + + + void test_SetProviderAddress() + { + const QString invalidProviderAddress("https://test.de/"); + QTest::ignoreMessage(QtWarningMsg, "Cannot select provider with address \"https://test.de/\""); + mModel->setProviderAddress(invalidProviderAddress); + QCOMPARE(mModel->mProvider.getAddress(), QString()); + + const QString validProviderAddress("https://makler.allianz.de"); + mModel->setProviderAddress(validProviderAddress); + QCOMPARE(mModel->mProvider.getAddress(), validProviderAddress); + } + + +}; + +QTEST_GUILESS_MAIN(test_ProviderNameFilterModel) +#include "test_ProviderNameFilterModel.moc" diff --git a/test/qt/qml/test_QmlFileStructure.cpp b/test/qt/ui/qml/test_QmlFileStructure.cpp similarity index 92% rename from test/qt/qml/test_QmlFileStructure.cpp rename to test/qt/ui/qml/test_QmlFileStructure.cpp index b9e950f..1c0737f 100644 --- a/test/qt/qml/test_QmlFileStructure.cpp +++ b/test/qt/ui/qml/test_QmlFileStructure.cpp @@ -67,7 +67,7 @@ class test_QmlFileStructure for (const QString& file : qAsConst(mQmlFiles)) { - if (file.contains(QStringLiteral("+phone")) || file.contains(QStringLiteral("+tablet"))) + if (file.contains(QLatin1String("+phone")) || file.contains(QLatin1String("+tablet"))) { const QFileInfo info(file); QTest::newRow(info.fileName().toLatin1().data()) << info; @@ -85,7 +85,7 @@ class test_QmlFileStructure dir.cdUp(); const auto& parentFolder = dir.dirName(); - if (parentFolder == QStringLiteral("+android") || parentFolder == QStringLiteral("+ios")) + if (parentFolder == QLatin1String("+android") || parentFolder == QLatin1String("+ios")) { dir.cdUp(); dir.cd(device); diff --git a/test/qt/ui/qml/test_RemoteServiceModel.cpp b/test/qt/ui/qml/test_RemoteServiceModel.cpp new file mode 100644 index 0000000..8b244d6 --- /dev/null +++ b/test/qt/ui/qml/test_RemoteServiceModel.cpp @@ -0,0 +1,194 @@ +/*! + * \brief Unit tests for \ref ProviderModel + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "RemoteServiceModel.h" + +#include "context/RemoteServiceContext.h" +#include "EstablishPaceChannelParser.h" +#include "MockRemoteServer.h" + +#include +#include + + +using namespace governikus; + +class test_RemoteServiceModel + : public QObject +{ + Q_OBJECT + RemoteServiceModel * mModel; + QSharedPointer mContext; + + static QByteArray createDataToParse(const PacePasswordId& pinId) + { + const QByteArray chat = QByteArray::fromHex("7F4C12060904007F00070301020253050000000F0F"); + const QByteArray certDescription = QByteArray::fromHex("30 8202A4" + " 06 0A 04007F00070301030103" + " A1 0E 0C0C442D547275737420476D6248" + " A3 3A 0C38476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E" + " A5 820248" + " 04 820244 4E616D652C20416E7363687269667420756E6420452D4D61696C2D4164726573736520646573204469656E737465616E626965746572733A0D0A476573616D7476657262616E64206465722064657574736368656E20566572736963686572756E67737769727473636861667420652E562E0D0A57696C68656C6D73747261C39F652034332F3433670D0A3130313137204265726C696E0D0A6265726C696E406764762E64650D0A0D0A4765736368C3A46674737A7765636B3A0D0A2D52656769737472696572756E6720756E64204C6F67696E20616D204744562D4D616B6C6572706F7274616C2D0D0A0D0A48696E7765697320617566206469652066C3BC722064656E204469656E737465616E626965746572207A757374C3A46E646967656E205374656C6C656E2C20646965206469652045696E68616C74756E672064657220566F7273636872696674656E207A756D20446174656E73636875747A206B6F6E74726F6C6C696572656E3A0D0A4265726C696E6572204265617566747261677465722066C3BC7220446174656E73636875747A20756E6420496E666F726D6174696F6E7366726569686569740D0A416E20646572205572616E696120342D31300D0A3130373837204265726C696E0D0A3033302F3133382038392D300D0A6D61696C626F7840646174656E73636875747A2D6265726C696E2E64650D0A687474703A2F2F7777772E646174656E73636875747A2D6265726C696E2E64650D0A416E737072656368706172746E65723A2044722E20416C6578616E64657220446978"); + + EstablishPaceChannel builder; + builder.setPasswordId(pinId); + builder.setChat(chat); + builder.setCertificateDescription(certDescription); + const auto command = builder.createCommandDataCcid(); + + return command.getBuffer(); + } + + + private Q_SLOTS: + void initTestCase() + { + Env::setCreator(std::function([&] { + return new MockRemoteServer(); + })); + mModel = Env::getSingleton(); + } + + + void init() + { + mContext.reset(new RemoteServiceContext()); + } + + + void cleanup() + { + mModel->resetContext(); + mContext.clear(); + } + + + void test_ReaderPlugInType() + { + const ReaderManagerPlugInType input1 = ReaderManagerPlugInType::NFC; + const ReaderManagerPlugInType input2 = ReaderManagerPlugInType::UNKNOWN; + + mModel->setReaderPlugInType(input1); + QCOMPARE(mModel->getReaderPlugInType(), ReaderManagerPlugInType::UNKNOWN); + + mModel->resetContext(mContext); + mModel->setReaderPlugInType(input1); + QCOMPARE(mModel->getReaderPlugInType(), input1); + + mModel->setReaderPlugInType(input2); + QCOMPARE(mModel->getReaderPlugInType(), ReaderManagerPlugInType::UNKNOWN); + } + + + void test_CancelPasswordRequest() + { + QSignalSpy spy(mContext.data(), &RemoteServiceContext::fireCancelPasswordRequest); + + mModel->cancelPasswordRequest(); + QCOMPARE(spy.count(), 0); + + mModel->resetContext(mContext); + mModel->cancelPasswordRequest(); + QCOMPARE(spy.count(), 1); + } + + + void test_Running() + { + QSignalSpy spyStartWorkflow(mModel, &RemoteServiceModel::fireStartWorkflow); + QSignalSpy spyCancelWorkflow(mContext.data(), &RemoteServiceContext::fireCancelWorkflow); + QSignalSpy spyIsRunningChanged(mModel, &RemoteServiceModel::fireIsRunningChanged); + + QVERIFY(!mModel->isRunning()); + + mModel->resetContext(mContext); + QVERIFY(!mModel->isRunning()); + + mContext->getRemoteServer()->start(QString()); + QVERIFY(mModel->isRunning()); + mModel->setRunning(false); + QCOMPARE(spyIsRunningChanged.count(), 1); + QCOMPARE(spyCancelWorkflow.count(), 1); + + mContext->getRemoteServer()->stop(); + mModel->setRunning(true); + QCOMPARE(spyIsRunningChanged.count(), 2); + QCOMPARE(spyStartWorkflow.count(), 1); + } + + + void test_ResetContext() + { + QSignalSpy spyConnectedChanged(mModel, &RemoteServiceModel::fireConnectedChanged); + QSignalSpy spyCurrentStateChanged(mModel, &WorkflowModel::fireCurrentStateChanged); + QSignalSpy spyIsRunningChanged(mModel, &RemoteServiceModel::fireIsRunningChanged); + QSignalSpy spyPskChanged(mModel, &RemoteServiceModel::firePskChanged); + QSignalSpy spyConnectedClientDeviceNameChanged(mModel, &RemoteServiceModel::fireConnectionInfoChanged); + + mModel->resetContext(mContext); + QCOMPARE(spyCurrentStateChanged.count(), 1); + QCOMPARE(spyConnectedChanged.count(), 1); + + Q_EMIT mContext->fireStateChanged(QString()); + QCOMPARE(spyIsRunningChanged.count(), 1); + + Q_EMIT mContext->getRemoteServer()->firePskChanged(QByteArray()); + QCOMPARE(spyPskChanged.count(), 1); + + Q_EMIT mContext->getRemoteServer()->fireConnectedChanged(true); + QCOMPARE(spyConnectedChanged.count(), 2); + QCOMPARE(spyConnectedClientDeviceNameChanged.count(), 1); + } + + + void test_SetPairing() + { + mModel->resetContext(mContext); + const QSharedPointer server = qSharedPointerCast(mContext->getRemoteServer()); + + mModel->setPairing(true); + QVERIFY(server->getPairing()); + + mModel->setPairing(false); + QVERIFY(!server->getPairing()); + } + + + void test_GetPasswordTypeContextIsNull() + { + QCOMPARE(mModel->getPasswordType(), QString()); + } + + + void test_GetPassword_data() + { + QTest::addColumn("password"); + QTest::addColumn("output"); + + QTest::newRow("pin") << PacePasswordId::PACE_PIN << QString("PIN"); + QTest::newRow("can") << PacePasswordId::PACE_CAN << QString("CAN"); + QTest::newRow("puk") << PacePasswordId::PACE_PUK << QString("PUK"); + QTest::newRow("unknown") << PacePasswordId::UNKNOWN << QString(); + } + + + void test_GetPassword() + { + QFETCH(PacePasswordId, password); + QFETCH(QString, output); + + auto data = createDataToParse(password); + QSharedPointer message(new IfdEstablishPaceChannel(QString(), data)); + mContext->setEstablishPaceChannelMessage(message); + mModel->resetContext(mContext); + QCOMPARE(mModel->getPasswordType(), output); + } + + +}; + +QTEST_GUILESS_MAIN(test_RemoteServiceModel) +#include "test_RemoteServiceModel.moc" diff --git a/test/qt/ui/qml/test_SelfAuthModel.cpp b/test/qt/ui/qml/test_SelfAuthModel.cpp new file mode 100644 index 0000000..f272e80 --- /dev/null +++ b/test/qt/ui/qml/test_SelfAuthModel.cpp @@ -0,0 +1,119 @@ +/*! + * \brief Unit tests for \ref SelfAuthModel + * + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "SelfAuthModel.h" + +#include "context/SelfAuthContext.h" + +#include "MockCardConnectionWorker.h" +#include "TestFileHelper.h" + +#include + + +using namespace governikus; + +class test_SelfAuthModel + : public QObject +{ + Q_OBJECT + QSharedPointer mContext; + SelfAuthModel* mModel; + + private Q_SLOTS: + void init() + { + mContext.reset(new SelfAuthContext()); + mModel = Env::getSingleton(); + } + + + void cleanup() + { + mContext.clear(); + mModel->resetContext(); + } + + + void test_StartWorkflow() + { + QSignalSpy spy(mModel, &SelfAuthModel::fireStartWorkflow); + + mModel->startWorkflow(); + QCOMPARE(spy.count(), 1); + } + + + void test_CancelWorkflow() + { + QSignalSpy spy(mContext.data(), &SelfAuthContext::fireCancelWorkflow); + + mModel->cancelWorkflow(); + QCOMPARE(spy.count(), 0); + + mModel->resetContext(mContext); + mModel->cancelWorkflow(); + QCOMPARE(spy.count(), 1); + } + + + void test_IsBasicReader() + { + + QThread workerThread; + workerThread.start(); + + QVERIFY(mModel->isBasicReader()); + + const QSharedPointer worker(new MockCardConnectionWorker()); + worker->moveToThread(&workerThread); + const QSharedPointer connection(new CardConnection(worker)); + mContext->setCardConnection(connection); + ReaderInfo info; + + info.setBasicReader(true); + Q_EMIT worker->fireReaderInfoChanged(info); + mModel->resetContext(mContext); + QVERIFY(mModel->isBasicReader()); + + info.setBasicReader(false); + Q_EMIT worker->fireReaderInfoChanged(info); + QVERIFY(!mModel->isBasicReader()); + + workerThread.quit(); + workerThread.wait(); + } + + + void test_RoleNames() + { + QVERIFY(mModel->roleNames().keys().contains(SelfAuthModel::DataRoles::NAME)); + QVERIFY(mModel->roleNames().keys().contains(SelfAuthModel::DataRoles::VALUE)); + QVERIFY(mModel->roleNames().values().contains("name")); + QVERIFY(mModel->roleNames().values().contains("value")); + } + + + void test_OnSelfAuthenticationDataChanged() + { + const auto& data = TestFileHelper::readFile(":/self/SelfAuthenticationDataNoAddress.json"); + SelfAuthenticationData selfAuthenticationData(data); + mContext->setSelfAuthenticationData(selfAuthenticationData); + mModel->resetContext(mContext); + for (int i = 0; i < selfAuthenticationData.getOrderedSelfData().length(); i++) + { + QModelIndex index = mModel->index(i, 0); + QCOMPARE(mModel->data(index, SelfAuthModel::DataRoles::NAME), selfAuthenticationData.getOrderedSelfData().at(i).first); + QCOMPARE(mModel->data(index, SelfAuthModel::DataRoles::VALUE), selfAuthenticationData.getOrderedSelfData().at(i).second); + QCOMPARE(mModel->data(index, 100), QVariant()); + } + } + + +}; + +QTEST_GUILESS_MAIN(test_SelfAuthModel) +#include "test_SelfAuthModel.moc" diff --git a/test/qt/ui/qml/test_UIPlugInQml.cpp b/test/qt/ui/qml/test_UIPlugInQml.cpp new file mode 100644 index 0000000..17ada66 --- /dev/null +++ b/test/qt/ui/qml/test_UIPlugInQml.cpp @@ -0,0 +1,172 @@ +/*! + * \copyright Copyright (c) 2019 Governikus GmbH & Co. KG, Germany + */ + + +#include "PortFile.h" +#include "WebSocketHelper.h" + +#include +#include +#include + +using namespace governikus; + + +class test_UIPlugInQml + : public QObject +{ + Q_OBJECT + + private: + static const int PROCESS_TIMEOUT = 75000; + + QScopedPointer mApp2; + QScopedPointer mHelper; + + bool isQmlEngineInitDone(const bool pCheckDoneAndSuccessful) + { + mHelper->sendMessage(QStringLiteral("{\"cmd\": \"GET_LOG\"}")); + + bool initContainedInLog = false; + bool initContainedAndSuccess = false; + QString logData; + mHelper->waitForMessage([&initContainedInLog, &initContainedAndSuccess, &logData](const QJsonObject& pMessage){ + if (pMessage[QLatin1String("data")].isNull()) + { + return false; + } + + const QString& data = pMessage[QLatin1String("data")].toString(); + logData = data; + initContainedInLog = data.contains(QLatin1String("QML engine initialization finished")); + initContainedAndSuccess = data.contains(QLatin1String("QML engine initialization finished with 0 warnings.")); + return true; + }); + + if (pCheckDoneAndSuccessful) + { + if (initContainedAndSuccess) + { + return true; + } + + qDebug().noquote() << "Error output from AusweisApp2 process:\n" << logData; + return false; + } + + return initContainedInLog; + } + + + private Q_SLOTS: + void initTestCase() + { + qRegisterMetaType("QProcess::ProcessState"); + } + + + void test_qmlEngineInit_data() + { + QTest::addColumn("platformSelector"); + + QTest::newRow("Android") << QString("mobile,android,phone"); + QTest::newRow("Android Tablet") << QString("mobile,android,tablet"); + QTest::newRow("iOS") << QString("mobile,ios,phone"); + QTest::newRow("iOS Tablet") << QString("mobile,ios,tablet"); + QTest::newRow("macOS") << QString("desktop,mac"); + QTest::newRow("Windows") << QString("desktop,win"); + } + + + void test_qmlEngineInit() + { + QFETCH(QString, platformSelector); + + const QString& path = QCoreApplication::applicationDirPath() + "/../../src/"; + const QString& app = path + "AusweisApp2" +#ifdef Q_OS_WIN + + ".exe" +#endif + ; + + QStringList args; + args << "--ui" << "qml"; + args << "--ui" << "websocket"; + args << "--port" << "0"; + args << "-platform" << "minimal"; + + mApp2.reset(new QProcess()); + mApp2->setProgram(app); + mApp2->setWorkingDirectory(path); + mApp2->setArguments(args); + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("OVERRIDE_PLATFORM_SELECTOR", platformSelector); + mApp2->setProcessEnvironment(env); + + mApp2->start(); + QVERIFY(mApp2->waitForStarted(PROCESS_TIMEOUT)); + + QFile portInfoFile(PortFile::getPortFilename(QString(), mApp2->processId(), QStringLiteral("AusweisApp2"))); + QTRY_COMPARE_WITH_TIMEOUT(portInfoFile.exists(), true, PROCESS_TIMEOUT); + QVERIFY(portInfoFile.open(QIODevice::ReadOnly)); + + quint16 webSocketPort = 0; + QTextStream(&portInfoFile) >> webSocketPort; + QVERIFY(webSocketPort > 0); + + mHelper.reset(new WebSocketHelper(webSocketPort)); + QCOMPARE(mHelper->getState(), QAbstractSocket::SocketState::ConnectedState); + + QTRY_VERIFY_WITH_TIMEOUT(isQmlEngineInitDone(false), PROCESS_TIMEOUT); + QVERIFY(isQmlEngineInitDone(true)); + } + + + void cleanup() + { + const QString portFile = PortFile::getPortFilename(QString(), mApp2->processId(), QStringLiteral("AusweisApp2")); + QVERIFY(QFile::exists(portFile)); + mHelper.reset(); + + if (mApp2->state() == QProcess::NotRunning) + { + return; + } + +#ifndef Q_OS_WIN + // QProcess::terminate() sends WM_CLOSE on Windows. We can not handle this signal + // since it does not clearly indicate a quit request. It might simply be a closing + // window, too. + mApp2->terminate(); + if (!mApp2->waitForFinished(PROCESS_TIMEOUT)) + { + QWARN("Application didn't terminate."); + } +#endif + + if (mApp2->state() != QProcess::NotRunning) + { +#ifndef Q_OS_WIN + QWARN("Killing application."); +#endif + mApp2->kill(); + } + QTRY_COMPARE_WITH_TIMEOUT(mApp2->state(), QProcess::NotRunning, PROCESS_TIMEOUT); + if (mApp2->exitStatus() != QProcess::NormalExit) + { + QWARN(mApp2->readAllStandardError().constData()); + } + +#ifdef Q_OS_WIN + QFile::remove(portFile); +#endif + QVERIFY(!QFile::exists(portFile)); + } + + +}; + +QTEST_GUILESS_MAIN(test_UIPlugInQml) +#include "test_UIPlugInQml.moc" diff --git a/test/qt/qml/test_WorkflowModel.cpp b/test/qt/ui/qml/test_WorkflowModel.cpp similarity index 100% rename from test/qt/qml/test_WorkflowModel.cpp rename to test/qt/ui/qml/test_WorkflowModel.cpp diff --git a/test/qt/websocket/test_UIPlugInWebSocket.cpp b/test/qt/ui/websocket/test_UIPlugInWebSocket.cpp similarity index 88% rename from test/qt/websocket/test_UIPlugInWebSocket.cpp rename to test/qt/ui/websocket/test_UIPlugInWebSocket.cpp index 2a9b705..a1ad24c 100644 --- a/test/qt/websocket/test_UIPlugInWebSocket.cpp +++ b/test/qt/ui/websocket/test_UIPlugInWebSocket.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include using namespace governikus; @@ -26,22 +26,17 @@ class test_UIPlugInWebSocket static const int PROCESS_TIMEOUT = 15000; QScopedPointer mApp2; - QScopedPointer mWebSocket; QScopedPointer mHelper; private Q_SLOTS: void initTestCase() { - #ifdef Q_OS_WIN - QSKIP("Not supported"); - #endif + qRegisterMetaType("QProcess::ProcessState"); } void init() { - qRegisterMetaType("QProcess::ProcessState"); - QString path = QCoreApplication::applicationDirPath() + "/../../src/"; QString app = path + "AusweisApp2"; #ifdef Q_OS_WIN @@ -85,20 +80,32 @@ class test_UIPlugInWebSocket QCOMPARE(mApp2->state(), QProcess::Running); + #ifndef Q_OS_WIN + // QProcess::terminate() sends WM_CLOSE on Windows. We can not handle this signal + // since it does not clearly indicate a quit request. It might simply be a closing + // window, too. mApp2->terminate(); mApp2->waitForFinished(PROCESS_TIMEOUT); + #endif if (mApp2->state() != QProcess::NotRunning) { mApp2->kill(); } - QCOMPARE(mApp2->state(), QProcess::NotRunning); + QTRY_COMPARE_WITH_TIMEOUT(mApp2->state(), QProcess::NotRunning, PROCESS_TIMEOUT); + #ifndef Q_OS_WIN + // There will never be a clean shutdown on Windows. if (mApp2->exitCode() != 0) { qDebug().noquote() << "Error output from AusweisApp2 process:\n" << mApp2->readAllStandardError(); } QCOMPARE(mApp2->exitCode(), 0); + #endif + + #ifdef Q_OS_WIN + QFile::remove(portFile); + #endif QVERIFY(!QFile::exists(portFile)); } diff --git a/test/qt/widget/test_HelpAction.cpp b/test/qt/ui/widget/test_HelpAction.cpp similarity index 99% rename from test/qt/widget/test_HelpAction.cpp rename to test/qt/ui/widget/test_HelpAction.cpp index 23e339c..ba6ecd9 100644 --- a/test/qt/widget/test_HelpAction.cpp +++ b/test/qt/ui/widget/test_HelpAction.cpp @@ -6,7 +6,7 @@ #include "TestFileHelper.h" -#include +#include #include "HelpAction.h" #include "LanguageLoader.h" diff --git a/test/qt/widget/test_ReaderDriverModel.cpp b/test/qt/ui/widget/test_ReaderDriverModel.cpp similarity index 99% rename from test/qt/widget/test_ReaderDriverModel.cpp rename to test/qt/ui/widget/test_ReaderDriverModel.cpp index 004c585..b8776d2 100644 --- a/test/qt/widget/test_ReaderDriverModel.cpp +++ b/test/qt/ui/widget/test_ReaderDriverModel.cpp @@ -39,7 +39,7 @@ MockReaderManager::MockReaderManager(const QVector& pReaderInfos) : QVector MockReaderManager::getReaderInfos(const ReaderFilter& pFilter) const { - Q_UNUSED(pFilter); + Q_UNUSED(pFilter) return mReaderInfos; } diff --git a/test/qt/whitelist_client/test_Survey.cpp b/test/qt/whitelist_client/test_Survey.cpp deleted file mode 100644 index 3b6dc0c..0000000 --- a/test/qt/whitelist_client/test_Survey.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/*! - * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany - */ - -#include "Survey.h" - -#include - -using namespace governikus; - - -class test_Survey - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void testValidInvalid() - { - const Survey nonNullSurvey( - QStringLiteral("37.0.A.2.248"), - QStringLiteral("6.0"), - QStringLiteral("3.18.19+"), - 128, - QStringLiteral("Sony"), - QStringLiteral("F3311"), - QStringLiteral("XPERIA E5"), - QStringLiteral("1.15.1")); - QVERIFY(!nonNullSurvey.isNull()); - - const Survey nullSurvey; - QVERIFY(nullSurvey.isNull()); - } - - - void testToJSON() - { - const Survey survey( - QStringLiteral("37.0.A.2.248"), - QStringLiteral("6.0"), - QStringLiteral("3.18.19+"), - 128, - QStringLiteral("Sony"), - QStringLiteral("F3311"), - QStringLiteral("XPERIA E5"), - QStringLiteral("1.15.1")); - - const QByteArray& byteArray = survey.toJsonByteArray(); - QCOMPARE(byteArray, - QByteArray("{\n" - " \"AusweisAppVersionNumber\": \"1.15.1\",\n" - " \"ModelName\": \"XPERIA E5\",\n" - " \"ModelNumber\": \"F3311\",\n" - " \"Rom\": {\n" - " \"AndroidVersion\": \"6.0\",\n" - " \"BuildNumber\": \"37.0.A.2.248\",\n" - " \"KernelVersion\": \"3.18.19+\",\n" - " \"MaximumNfcPacketLength\": 128\n" - " },\n" - " \"Vendor\": \"Sony\"\n" - "}\n")); - } - - -}; - -QTEST_GUILESS_MAIN(test_Survey) -#include "test_Survey.moc" diff --git a/test/qt/whitelist_client/test_SurveyModel.cpp b/test/qt/whitelist_client/test_SurveyModel.cpp new file mode 100644 index 0000000..3edf6f1 --- /dev/null +++ b/test/qt/whitelist_client/test_SurveyModel.cpp @@ -0,0 +1,84 @@ +/*! + * \copyright Copyright (c) 2018-2019 Governikus GmbH & Co. KG, Germany + */ + +#include "SurveyModel.h" + +#include "DeviceInfo.h" +#include "Env.h" +#include "MockNetworkManager.h" + +#include +#include + +using namespace governikus; + +class test_SurveyModel + : public QObject +{ + Q_OBJECT + + private: + QPointer mNetworkManager; + + private Q_SLOTS: + void init() + { + mNetworkManager = new MockNetworkManager(); + Env::set(NetworkManager::staticMetaObject, mNetworkManager.data()); + } + + + void testTransmitSurvey() + { + SurveyModel model; + + model.transmitSurvey(); + const QByteArray json = mNetworkManager->getLastData(); + auto result = QJsonDocument::fromJson(json); + + QVERIFY(result["Rom"].isObject()); + auto rom = result["Rom"].toObject(); + QVERIFY(rom["BuildNumber"].isString()); + QCOMPARE(rom["BuildNumber"].toString(), DeviceInfo::getOSBuildNumber()); + QVERIFY(rom["AndroidVersion"].isString()); + QCOMPARE(rom["AndroidVersion"].toString(), DeviceInfo::getOSVersion()); + QVERIFY(rom["KernelVersion"].isString()); + QCOMPARE(rom["KernelVersion"].toString(), DeviceInfo::getKernelVersion()); + QVERIFY(rom["MaximumNfcPacketLength"].isDouble()); + QCOMPARE(rom["MaximumNfcPacketLength"].toDouble(), 0); + + QVERIFY(result["Vendor"].isString()); + QCOMPARE(result["Vendor"].toString(), DeviceInfo::getVendor()); + QVERIFY(result["ModelNumber"].isString()); + QCOMPARE(result["ModelNumber"].toString(), DeviceInfo::getModelNumber()); + QVERIFY(result["ModelName"].isString()); + QCOMPARE(result["ModelName"].toString(), DeviceInfo::getModelName()); + QVERIFY(result["AusweisAppVersionNumber"].isString()); + QCOMPARE(result["AusweisAppVersionNumber"].toString(), QCoreApplication::applicationVersion()); + } + + + void testMaximumPacketLength() + { + SurveyModel model; + + model.transmitSurvey(); + QByteArray json = mNetworkManager->getLastData(); + auto result = QJsonDocument::fromJson(json); + auto rom = result["Rom"].toObject(); + QCOMPARE(rom["MaximumNfcPacketLength"].toDouble(), 0); + + model.setMaximumNfcPacketLength(9999); + model.transmitSurvey(); + json = mNetworkManager->getLastData(); + result = QJsonDocument::fromJson(json); + rom = result["Rom"].toObject(); + QCOMPARE(rom["MaximumNfcPacketLength"].toDouble(), 9999); + } + + +}; + +QTEST_GUILESS_MAIN(test_SurveyModel) +#include "test_SurveyModel.moc" diff --git a/uncrustify.cfg b/uncrustify.cfg index 0513850..5a69ccc 100644 --- a/uncrustify.cfg +++ b/uncrustify.cfg @@ -1,4 +1,4 @@ -# Uncrustify-0.68_f +# Uncrustify-0.69.0_f newlines = lf input_tab_size = 4 output_tab_size = 4 @@ -56,6 +56,7 @@ sp_before_template_paren = ignore sp_template_angle = remove sp_before_angle = remove sp_inside_angle = remove +sp_inside_angle_empty = ignore sp_angle_colon = ignore sp_after_angle = force sp_angle_paren = remove @@ -91,7 +92,10 @@ sp_between_mdatype_commas = ignore sp_paren_comma = force sp_before_ellipsis = ignore sp_type_ellipsis = ignore +sp_type_question = ignore sp_paren_ellipsis = ignore +sp_paren_qualifier = ignore +sp_paren_noexcept = ignore sp_after_class_colon = force sp_before_class_colon = remove sp_after_constr_colon = ignore @@ -145,6 +149,7 @@ sp_throw_paren = remove sp_after_throw = ignore sp_catch_paren = force sp_oc_catch_paren = ignore +sp_oc_classname_paren = ignore sp_version_paren = ignore sp_scope_paren = ignore sp_super_paren = remove @@ -219,6 +224,7 @@ sp_num_before_tr_emb_cmt = 0 sp_annotation_paren = ignore sp_skip_vbrace_tokens = false sp_after_noexcept = ignore +sp_vala_after_translation = ignore force_tab_after_define = false indent_columns = 4 indent_continue = 8 @@ -272,6 +278,7 @@ indent_switch_pp = true indent_case_shift = 0 indent_case_brace = 0 indent_col1_comment = true +indent_col1_multi_string_literal = false indent_label = 1 indent_access_spec = 0 indent_access_spec_body = true @@ -331,13 +338,6 @@ nl_assign_brace = remove nl_assign_square = ignore nl_tsquare_brace = ignore nl_after_square_assign = ignore -nl_func_var_def_blk = 0 -nl_typedef_blk_start = 0 -nl_typedef_blk_end = 0 -nl_typedef_blk_in = 0 -nl_var_def_blk_start = 0 -nl_var_def_blk_end = 0 -nl_var_def_blk_in = 0 nl_fcall_brace = ignore nl_enum_brace = force nl_enum_class = remove @@ -415,11 +415,13 @@ nl_func_def_end_multi_line = false nl_func_decl_empty = ignore nl_func_def_empty = ignore nl_func_call_empty = ignore +nl_func_call_start = ignore nl_func_call_start_multi_line = false nl_func_call_args_multi_line = false nl_func_call_end_multi_line = false nl_oc_msg_args = false nl_fdef_brace = force +nl_fdef_brace_cond = ignore nl_cpp_ldef_brace = ignore nl_return_expr = remove nl_after_semicolon = true @@ -450,6 +452,8 @@ nl_before_synchronized = ignore nl_after_synchronized = ignore nl_before_do = ignore nl_after_do = ignore +nl_before_return = false +nl_after_return = true nl_ds_struct_enum_cmt = false nl_ds_struct_enum_close_brace = false nl_class_colon = force @@ -476,6 +480,13 @@ nl_class_leave_one_liner_groups = false nl_after_func_body = 3 nl_after_func_body_class = 0 nl_after_func_body_one_liner = 0 +nl_func_var_def_blk = 0 +nl_typedef_blk_start = 0 +nl_typedef_blk_end = 0 +nl_typedef_blk_in = 0 +nl_var_def_blk_start = 0 +nl_var_def_blk_end = 0 +nl_var_def_blk_in = 0 nl_before_block_comment = 2 nl_before_c_comment = 0 nl_before_cpp_comment = 0 @@ -495,8 +506,6 @@ nl_inside_namespace = 0 eat_blanks_after_open_brace = false eat_blanks_before_close_brace = false nl_remove_extra_newlines = 0 -nl_before_return = false -nl_after_return = true nl_after_annotation = ignore nl_between_annotation = ignore pos_arith = ignore @@ -523,6 +532,9 @@ align_func_params = false align_func_params_span = 0 align_func_params_thresh = 0 align_func_params_gap = 0 +align_constr_value_span = 0 +align_constr_value_thresh = 0 +align_constr_value_gap = 0 align_same_func_call_params = false align_same_func_call_params_span = 0 align_same_func_call_params_thresh = 0 @@ -536,6 +548,7 @@ align_var_def_colon_gap = 0 align_var_def_attribute = false align_var_def_inline = false align_assign_span = 0 +align_assign_func_proto_span = 0 align_assign_thresh = 0 align_assign_decl_func = 0 align_enum_equ_span = 0 @@ -547,17 +560,18 @@ align_var_struct_span = 0 align_var_struct_thresh = 0 align_var_struct_gap = 0 align_struct_init_span = 0 -align_typedef_gap = 0 align_typedef_span = 0 +align_typedef_gap = 0 align_typedef_func = 0 align_typedef_star_style = 0 align_typedef_amp_style = 0 align_right_cmt_span = 0 +align_right_cmt_gap = 0 align_right_cmt_mix = false align_right_cmt_same_level = false -align_right_cmt_gap = 0 align_right_cmt_at_col = 0 align_func_proto_span = 0 +align_func_proto_thresh = 0 align_func_proto_gap = 0 align_on_operator = false align_mix_var_proto = false @@ -567,8 +581,8 @@ align_single_line_brace_gap = 0 align_oc_msg_spec_span = 0 align_nl_cont = false align_pp_define_together = false -align_pp_define_gap = 0 align_pp_define_span = 0 +align_pp_define_gap = 0 align_left_shift = true align_asm_colon = false align_oc_msg_colon_span = 0 @@ -658,6 +672,5 @@ use_options_overriding_for_qt_macros = true warn_level_tabs_found_in_verbatim_string_literals = 2 macro-open ASN1_ITEM_TEMPLATE macro-close ASN1_ITEM_TEMPLATE_END -set PRIVATE Q_SIGNALS # option(s) with 'not default' value: 157 #